<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Trung Vo's RSS Feed]]></title><description><![CDATA[Web expert with 10 years of experience, Google Developer Expert in Angular and Web Technologies (Performance), fosters web dev communities and speaks globally, based in Singapore.]]></description><link>http://github.com/dylang/node-rss</link><generator>GatsbyJS</generator><lastBuildDate>Sat, 13 Jun 2026 14:44:15 GMT</lastBuildDate><item><title><![CDATA[Upgrading to Angular 20: more AI gotcha than I expected]]></title><link>https://trungvose.comangular-spotify-upgrade-angular-17-to-angular-20-part0-planning/</link><guid isPermaLink="false">https://trungvose.comangular-spotify-upgrade-angular-17-to-angular-20-part0-planning/</guid><pubDate>Sun, 19 Apr 2026 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After finishing &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part7-angular-20/&quot;&gt;the Jira Clone upgrade to Angular 20&lt;/a&gt;, I opened &lt;a href=&quot;https://github.com/trungvose/angular-spotify&quot;&gt;Angular Spotify&lt;/a&gt; and saw it was still on &lt;strong&gt;Angular 17&lt;/strong&gt;. Same problem, different repo.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b8216838631ff142a88cb7089da32656/9568a/00-spotify-version.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdl2gKD/xAAXEAADAQAAAAAAAAAAAAAAAAAAARAR/9oACAEBAAEFAro5/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhAAAgIDAAAAAAAAAAAAAAAAABEBURAhQf/aAAgBAQABPyHdCsUEMc4//9oADAMBAAIAAwAAABD3z//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ECf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEBAQACAwEAAAAAAAAAAAABEQBBUSExYZH/2gAIAQEAAT8QJIeHcxSNHfGONf11qST7lFSzHrf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Spotify at Angular 17, target 20&quot;
        title=&quot;Angular Spotify at Angular 17, target 20&quot;
        src=&quot;/static/b8216838631ff142a88cb7089da32656/6a068/00-spotify-version.jpg&quot;
        srcset=&quot;/static/b8216838631ff142a88cb7089da32656/09b79/00-spotify-version.jpg 240w,
/static/b8216838631ff142a88cb7089da32656/7cc5e/00-spotify-version.jpg 480w,
/static/b8216838631ff142a88cb7089da32656/6a068/00-spotify-version.jpg 960w,
/static/b8216838631ff142a88cb7089da32656/644c5/00-spotify-version.jpg 1440w,
/static/b8216838631ff142a88cb7089da32656/0f98f/00-spotify-version.jpg 1920w,
/static/b8216838631ff142a88cb7089da32656/9568a/00-spotify-version.jpg 3024w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;No Angular upgrade happens in this post. Two things came up during planning that are worth writing about. Claude gave confident answers that turned out to be wrong once I pushed back:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It insisted RxJS 7 was required before Angular 18. It is not, RxJS 6 still works.&lt;/li&gt;
&lt;li&gt;It cited a Sentry migration guide for a pattern that is not on that page. The actual reference is in the package README.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are using AI for a migration (which is very likely a yes), those two sections are the main point to watch out for.&lt;/p&gt;
&lt;h2 id=&quot;1-where-angular-spotify-stands-today&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-where-angular-spotify-stands-today&quot; aria-label=&quot;1 where angular spotify stands today permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Where Angular Spotify stands today&lt;/h2&gt;
&lt;p&gt;Angular Spotify has drifted more than &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; had. Current state:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Current&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;17.3.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target: &lt;code class=&quot;language-text&quot;&gt;20.x&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@nx/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;18.2.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nx already ahead of Angular, fine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@ngrx/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;17.0.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tracks Angular majors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;15.1.0&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Peer deps &lt;code class=&quot;language-text&quot;&gt;@angular/core@^15.0.1&lt;/code&gt;; highest-risk upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;rxjs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;6.6.6&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Last release &lt;a href=&quot;https://www.npmjs.com/package/rxjs/v/6.6.7&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;6.6.7&lt;/code&gt;&lt;/a&gt; in 2021; &lt;a href=&quot;#33-gotcha-2-rxjs-is-not-actually-required-for-angular-18--pr-114&quot;&gt;see 3.3&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;zone.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;0.14.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fine for 17/18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@sentry/angular&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;7.49.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plus &lt;code class=&quot;language-text&quot;&gt;@sentry/tracing@6.8.0&lt;/code&gt;, deprecated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;jest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;29.4.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plan to migrate to Vitest at Angular 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;eslint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;8.57.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flat config + ESLint 9 at Angular 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;cypress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;7.6.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listed as a dep, but &lt;strong&gt;no e2e tests exist&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;tailwindcss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;3.3.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stay on v3 (lesson from Jira Clone Part 7)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;On the surface it looks fine: Angular 17, Nx 18, NgRx 17. The issue is &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd@15.1.0&lt;/code&gt; where the peer deps only support &lt;code class=&quot;language-text&quot;&gt;@angular/core@^15.0.1&lt;/code&gt; and we have been running Angular 17. Yarn tolerated the mismatch silently. If we were using npm, we would not have gone this far 😂&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rxjs@6.6.6&lt;/code&gt; also looked like a blocker. Claude said it was, it is not hehe. More on that in &lt;a href=&quot;#33-gotcha-2-rxjs-is-not-actually-required-for-angular-18--pr-114&quot;&gt;3.3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Going straight to Angular 20 means upgrading seven or eight packages across three Angular majors in one PR which is too risky. I am doing it as a series instead: &lt;strong&gt;17 → 18 → 19 → 20&lt;/strong&gt;, one major per PR, one post per step.&lt;/p&gt;
&lt;p&gt;Before the Angular upgrade, there are four prep PRs to land first. That is what this post is about.&lt;/p&gt;
&lt;h2 id=&quot;2-kicking-off-claude-code-for-planning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-kicking-off-claude-code-for-planning&quot; aria-label=&quot;2 kicking off claude code for planning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Kicking off Claude Code for planning&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2ec519d6c8218a206385527eaac2b2d7/ea091/00-claude-session.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABy5CQf//EABUQAQEAAAAAAAAAAAAAAAAAAAAh/9oACAEBAAEFAoiP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAABBB/9oACAEBAAY/Air/xAAZEAABBQAAAAAAAAAAAAAAAAAAARFxgfH/2gAIAQEAAT8hWQ2BY//aAAwDAQACAAMAAAAQA8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAQUAAAAAAAAAAAAAAAABADFRYXGR/9oACAEBAAE/EDg0RvQQNeZ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude Code planning prompt for Angular Spotify&quot;
        title=&quot;Claude Code planning prompt for Angular Spotify&quot;
        src=&quot;/static/2ec519d6c8218a206385527eaac2b2d7/6a068/00-claude-session.jpg&quot;
        srcset=&quot;/static/2ec519d6c8218a206385527eaac2b2d7/09b79/00-claude-session.jpg 240w,
/static/2ec519d6c8218a206385527eaac2b2d7/7cc5e/00-claude-session.jpg 480w,
/static/2ec519d6c8218a206385527eaac2b2d7/6a068/00-claude-session.jpg 960w,
/static/2ec519d6c8218a206385527eaac2b2d7/644c5/00-claude-session.jpg 1440w,
/static/2ec519d6c8218a206385527eaac2b2d7/0f98f/00-claude-session.jpg 1920w,
/static/2ec519d6c8218a206385527eaac2b2d7/ea091/00-claude-session.jpg 2590w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Same opening move as &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part7-angular-20/&quot;&gt;Part 7 of Jira Clone&lt;/a&gt;. I started with the plan, not the code. First prompt was roughly: &lt;em&gt;Plan Angular 20 migration. I also want to write a blog post. Read my existing post to match the style.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It was back-and-forth, not a one-shot plan. Claude asked questions, I picked options. A few key decisions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Strategy:&lt;/strong&gt; Big-bang 17 → 20 in one PR, or stepwise? Went with stepwise. Smaller PRs, easier rollback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blog post shape:&lt;/strong&gt; One giant post or one per Angular upgrade? One per upgrade. Three upgrades in one post loses the per-step detail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timing:&lt;/strong&gt; Write the post as a guide upfront, or as a post-mortem after? Post-mortem. You want to write what actually happened, not what you planned.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then Claude audited the repo and gave the assessment: which packages block the Angular upgrade, and which are just debt to clean up along the way.&lt;/p&gt;
&lt;h2 id=&quot;3-four-prep-prs-peer-dependency-analysis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-four-prep-prs-peer-dependency-analysis&quot; aria-label=&quot;3 four prep prs peer dependency analysis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Four prep PRs: peer-dependency analysis&lt;/h2&gt;
&lt;p&gt;Before touching Angular, figure out which other packages are tied to the Angular version and which are not. For example, &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt; is tied — it only supports Angular 15. &lt;code class=&quot;language-text&quot;&gt;rxjs&lt;/code&gt; is not — it works across Angular 17 through 20.&lt;/p&gt;
&lt;p&gt;Four packages kept coming up: &lt;code class=&quot;language-text&quot;&gt;@sentry/angular&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;rxjs&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;cypress&lt;/code&gt;. For each one, the question was: does this block the Angular upgrade, or is it just debt I want to clean up anyway? One command answers that: &lt;code class=&quot;language-text&quot;&gt;npm view &amp;lt;pkg&gt;@&amp;lt;version&gt; peerDependencies&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;peerDependencies&lt;/code&gt; is how a package tells you which version of its dependencies it expects you to have. For example, &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd@15&lt;/code&gt; expects &lt;code class=&quot;language-text&quot;&gt;@angular/core@^15.0.1&lt;/code&gt; — meaning it will &lt;em&gt;might&lt;/em&gt; work correctly if you run it with Angular 17 or 18.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After two corrections (more on those below), this is Claude’s final classification:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;PR&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;What peer deps say&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2.1&lt;/td&gt;
&lt;td&gt;Sentry: drop &lt;code class=&quot;language-text&quot;&gt;@sentry/tracing&lt;/code&gt;, bump &lt;code class=&quot;language-text&quot;&gt;@sentry/angular&lt;/code&gt; to v8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Blocker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@sentry/angular@7.49&lt;/code&gt; caps &lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt; at &lt;code class=&quot;language-text&quot;&gt;&amp;lt;= 15.x&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.2&lt;/td&gt;
&lt;td&gt;ng-zorro 15 → 17&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Blocker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd@15&lt;/code&gt; caps &lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt; at &lt;code class=&quot;language-text&quot;&gt;^15.0.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.3&lt;/td&gt;
&lt;td&gt;RxJS 6 → 7&lt;/td&gt;
&lt;td&gt;Non-blocker&lt;/td&gt;
&lt;td&gt;No peer-dep conflict, &lt;a href=&quot;#33-gotcha-2-rxjs-is-not-actually-required-for-angular-18--pr-114&quot;&gt;see 3.3&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.4&lt;/td&gt;
&lt;td&gt;Cypress removal&lt;/td&gt;
&lt;td&gt;Non-blocker&lt;/td&gt;
&lt;td&gt;No peer-dep conflict&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;31-sentry-blocker--pr-112&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31-sentry-blocker--pr-112&quot; aria-label=&quot;31 sentry blocker  pr 112 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1 Sentry (blocker) · &lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/112&quot;&gt;PR #112&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @sentry/angular@7.49.0 peerDependencies
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  rxjs: &lt;span class=&quot;token string&quot;&gt;&apos;^6.5.5 || ^7.x&apos;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/core&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&gt;= 10.x &amp;lt;= 15.x&apos;&lt;/span&gt;,      &lt;span class=&quot;token comment&quot;&gt;# caps at Angular 15&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/common&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&gt;= 10.x &amp;lt;= 15.x&apos;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/router&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&gt;= 10.x &amp;lt;= 15.x&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@sentry/angular@7.49.0&lt;/code&gt; caps Angular at 15.x. We have been on Angular 17 for months because yarn allowed the mismatch.&lt;/p&gt;
&lt;p&gt;There is also a second issue: &lt;code class=&quot;language-text&quot;&gt;@sentry/tracing@6.8.0&lt;/code&gt; has no peer deps so nothing warned, but it pulls &lt;code class=&quot;language-text&quot;&gt;@sentry/*@6.8&lt;/code&gt; internals while &lt;code class=&quot;language-text&quot;&gt;@sentry/angular@7.49&lt;/code&gt; pulls &lt;code class=&quot;language-text&quot;&gt;@sentry/*@7.49&lt;/code&gt;. We have been shipping two different Sentry versions in one bundle without knowing it.&lt;/p&gt;
&lt;h3 id=&quot;31a-gotcha-1-right-pattern-wrong-source&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31a-gotcha-1-right-pattern-wrong-source&quot; aria-label=&quot;31a gotcha 1 right pattern wrong source permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1.a Gotcha 1: right pattern, wrong source&lt;/h3&gt;
&lt;p&gt;The Sentry prep PR looked small on paper. Two files: &lt;code class=&quot;language-text&quot;&gt;main.ts&lt;/code&gt; for the v8 init API, and &lt;code class=&quot;language-text&quot;&gt;web-shell.module.ts&lt;/code&gt;. I &lt;em&gt;did not&lt;/em&gt; expect was a new block in &lt;code class=&quot;language-text&quot;&gt;web-shell.module.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; provide&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Sentry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TraceService&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Router&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  provide&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;APP_INITIALIZER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;useFactory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  deps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Sentry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TraceService&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  multi&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8207315150b3c20e09c60ab48483b67e/cecd0/05-sentry-app-init.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUBBAb/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABVaJQ6otEEv8A/8QAGhABAAMAAwAAAAAAAAAAAAAAAQACBBIUIf/aAAgBAQABBQL0mVDMXrCpM4dfjWf/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwEZ/8QAFxEAAwEAAAAAAAAAAAAAAAAAAQIQEf/aAAgBAgEBPwErk//EABsQAAICAwEAAAAAAAAAAAAAAAABAiERMpEx/9oACAEBAAY/AqI5aNl08I0arh//xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhMUEQkf/aAAgBAQABPyE6KlXze2uxJj4vGUKEt3nsA1F//9oADAMBAAIAAwAAABCQ7//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/EBZ//8QAFhEBAQEAAAAAAAAAAAAAAAAAARAh/9oACAECAQE/EE0z/8QAHBABAAICAwEAAAAAAAAAAAAAAQARIUFRYdEx/9oACAEBAAE/EElikzRPqamy2cyyLOvSNu7Y2sOrqw4ZcwSgHQ8n/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sentry TraceService and APP_INITIALIZER block added by Claude&quot;
        title=&quot;Sentry TraceService and APP_INITIALIZER block added by Claude&quot;
        src=&quot;/static/8207315150b3c20e09c60ab48483b67e/6a068/05-sentry-app-init.jpg&quot;
        srcset=&quot;/static/8207315150b3c20e09c60ab48483b67e/09b79/05-sentry-app-init.jpg 240w,
/static/8207315150b3c20e09c60ab48483b67e/7cc5e/05-sentry-app-init.jpg 480w,
/static/8207315150b3c20e09c60ab48483b67e/6a068/05-sentry-app-init.jpg 960w,
/static/8207315150b3c20e09c60ab48483b67e/644c5/05-sentry-app-init.jpg 1440w,
/static/8207315150b3c20e09c60ab48483b67e/0f98f/05-sentry-app-init.jpg 1920w,
/static/8207315150b3c20e09c60ab48483b67e/cecd0/05-sentry-app-init.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I asked Claude why the block was there. The answer was confident and came with a URL:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is the pattern Sentry’s v8 Angular migration guide recommends. See here: &lt;a href=&quot;https://docs.sentry.io/platforms/javascript/guides/angular/migration/v7-to-v8/&quot;&gt;https://docs.sentry.io/platforms/javascript/guides/angular/migration/v7-to-v8/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sounded ok, then I opened the link. There is nothing on that page about &lt;code class=&quot;language-text&quot;&gt;TraceService&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;APP_INITIALIZER&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So I pushed back: &lt;em&gt;“I do not see &lt;code class=&quot;language-text&quot;&gt;Sentry.TraceService&lt;/code&gt; mentioned on that page, are you sure?”&lt;/em&gt; Claude then ran &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; against the actual Sentry docs, grepped for the terms, and came back with the correction.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/35c670167ae85141d9b165eea4c91e0c/8ec0a/06-wrong-citation.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAEDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVYKzH/xAAWEAEBAQAAAAAAAAAAAAAAAAAAISD/2gAIAQEAAQUCRMf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAACAwAAAAAAAAAAAAAAAAAAASAxMv/aAAgBAQAGPwItmnD/xAAZEAEBAAMBAAAAAAAAAAAAAAABACFRkYH/2gAIAQEAAT8h9jDPKdOEEhN//9oADAMBAAIAAwAAABDwH//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAQACAgMBAAAAAAAAAAAAAAEAESFRMYGx8P/aAAgBAQABPxDKXa7geGdFj2UvA+7gJ3BFzBn/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude citing the Sentry v7 to v8 migration guide as the source&quot;
        title=&quot;Claude citing the Sentry v7 to v8 migration guide as the source&quot;
        src=&quot;/static/35c670167ae85141d9b165eea4c91e0c/6a068/06-wrong-citation.jpg&quot;
        srcset=&quot;/static/35c670167ae85141d9b165eea4c91e0c/09b79/06-wrong-citation.jpg 240w,
/static/35c670167ae85141d9b165eea4c91e0c/7cc5e/06-wrong-citation.jpg 480w,
/static/35c670167ae85141d9b165eea4c91e0c/6a068/06-wrong-citation.jpg 960w,
/static/35c670167ae85141d9b165eea4c91e0c/644c5/06-wrong-citation.jpg 1440w,
/static/35c670167ae85141d9b165eea4c91e0c/0f98f/06-wrong-citation.jpg 1920w,
/static/35c670167ae85141d9b165eea4c91e0c/8ec0a/06-wrong-citation.jpg 2560w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There is actually a mention of &lt;code class=&quot;language-text&quot;&gt;TraceService&lt;/code&gt; and the &lt;code class=&quot;language-text&quot;&gt;APP_INITIALIZER&lt;/code&gt; pattern in the Sentry docs, but it is in the README at &lt;a href=&quot;https://github.com/getsentry/sentry-javascript/blob/8.55.1/packages/angular/README.md#tracing&quot;&gt;sentry-javascript/8.55.1/angular/README.md#tracing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/63019731e5ba90f14998fcd637e9d999/cecd0/07-sentry-readme.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAQBAgMF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAAB5NW10cyAr//EABkQAQACAwAAAAAAAAAAAAAAAAABEQISMv/aAAgBAQABBQJa5awy6f/EABURAQEAAAAAAAAAAAAAAAAAABEQ/9oACAEDAQE/AVn/xAAXEQADAQAAAAAAAAAAAAAAAAABAhAR/9oACAECAQE/ASuT/8QAFhAAAwAAAAAAAAAAAAAAAAAAECAx/9oACAEBAAY/AhU//8QAGxAAAgIDAQAAAAAAAAAAAAAAAAERYRAhMZH/2gAIAQEAAT8ha0SjrLHpWKpLH//aAAwDAQACAAMAAAAQJ+//xAAWEQEBAQAAAAAAAAAAAAAAAAABECH/2gAIAQMBAT8QDgn/xAAWEQEBAQAAAAAAAAAAAAAAAAABECH/2gAIAQIBAT8QTTP/xAAcEAEAAgIDAQAAAAAAAAAAAAABABExYUFRcZH/2gAIAQEAAT8QqtcpoAaWdH1RCnjtgBABgPIGfZ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;TraceService pattern documented in the @sentry/angular package README&quot;
        title=&quot;TraceService pattern documented in the @sentry/angular package README&quot;
        src=&quot;/static/63019731e5ba90f14998fcd637e9d999/6a068/07-sentry-readme.jpg&quot;
        srcset=&quot;/static/63019731e5ba90f14998fcd637e9d999/09b79/07-sentry-readme.jpg 240w,
/static/63019731e5ba90f14998fcd637e9d999/7cc5e/07-sentry-readme.jpg 480w,
/static/63019731e5ba90f14998fcd637e9d999/6a068/07-sentry-readme.jpg 960w,
/static/63019731e5ba90f14998fcd637e9d999/644c5/07-sentry-readme.jpg 1440w,
/static/63019731e5ba90f14998fcd637e9d999/0f98f/07-sentry-readme.jpg 1920w,
/static/63019731e5ba90f14998fcd637e9d999/cecd0/07-sentry-readme.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I want to remember from this one.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I treated a URL as confirmation. The URL did not support the claim. A citation is only a citation if you open it. When an AI cites a URL, open the URL.&lt;/li&gt;
&lt;li&gt;When an AI adds code that the original did not have, ask why.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-ng-zorro-blocker--pr-113&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#32-ng-zorro-blocker--pr-113&quot; aria-label=&quot;32 ng zorro blocker  pr 113 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.2 ng-zorro (blocker) · &lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/113&quot;&gt;PR #113&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view ng-zorro-antd@15.1.0 peerDependencies
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/core&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;^15.0.1&apos;&lt;/span&gt;,              &lt;span class=&quot;token comment&quot;&gt;# caps at Angular 15&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/forms&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;^15.0.1&apos;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/common&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;^15.0.1&apos;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/router&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;^15.0.1&apos;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/animations&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;^15.0.1&apos;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&apos;@angular/platform-browser&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;^15.0.1&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Same as Sentry, but worse. &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd@15&lt;/code&gt; pins Angular to &lt;code class=&quot;language-text&quot;&gt;^15.0.1&lt;/code&gt; and we are on Angular 17. It has been running on an unsupported Angular version the whole time.&lt;/p&gt;
&lt;p&gt;This is the highest-risk PR. ng-zorro touches around several files: player controls, lyrics toggle, modals, toasts. Bumping 15 → 17 means two majors of breaking changes at once: theme tokens, form control API, modal service signatures, and the usual &lt;code class=&quot;language-text&quot;&gt;nz-select&lt;/code&gt; template quirks.&lt;/p&gt;
&lt;p&gt;Why 17 and not 18? To keep the PR manageable. One ng-zorro major per Angular step: 15 → 17 now, then 17 → 18, 18 → 19, 19 → 20 alongside each Angular upgrade.&lt;/p&gt;
&lt;h3 id=&quot;33-gotcha-2-rxjs-is-not-actually-required-for-angular-18--pr-114&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#33-gotcha-2-rxjs-is-not-actually-required-for-angular-18--pr-114&quot; aria-label=&quot;33 gotcha 2 rxjs is not actually required for angular 18  pr 114 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.3 Gotcha 2: RxJS is not actually required for Angular 18 · &lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/114&quot;&gt;PR #114&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Claude’s first take was confident:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PR A, RxJS 6 → 7. Angular 18 requires it; doing it on 17 isolates RxJS breakage from Angular breakage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c9e140e936e307eeac4a79a87e9c2b3f/cecd0/01-rxjs-wrong-assumption.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAByqtixQwP/8QAFxAAAwEAAAAAAAAAAAAAAAAAABARAf/aAAgBAQABBQJxTD//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwGq/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8BiP/EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAQADAQAAAAAAAAAAAAAAAAABEYFh/9oACAEBAAE/IdTq5UVDk//aAAwDAQACAAMAAAAQYA//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxCEP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/ELW//8QAHRAAAgIBBQAAAAAAAAAAAAAAAREAIZExUXGh0f/aAAgBAQABPxArc+YwFHKKpyvTuOFvJhZ6M//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude&amp;#39;s initial claim that RxJS 7 is required for Angular 18&quot;
        title=&quot;Claude&amp;#39;s initial claim that RxJS 7 is required for Angular 18&quot;
        src=&quot;/static/c9e140e936e307eeac4a79a87e9c2b3f/6a068/01-rxjs-wrong-assumption.jpg&quot;
        srcset=&quot;/static/c9e140e936e307eeac4a79a87e9c2b3f/09b79/01-rxjs-wrong-assumption.jpg 240w,
/static/c9e140e936e307eeac4a79a87e9c2b3f/7cc5e/01-rxjs-wrong-assumption.jpg 480w,
/static/c9e140e936e307eeac4a79a87e9c2b3f/6a068/01-rxjs-wrong-assumption.jpg 960w,
/static/c9e140e936e307eeac4a79a87e9c2b3f/644c5/01-rxjs-wrong-assumption.jpg 1440w,
/static/c9e140e936e307eeac4a79a87e9c2b3f/0f98f/01-rxjs-wrong-assumption.jpg 1920w,
/static/c9e140e936e307eeac4a79a87e9c2b3f/cecd0/01-rxjs-wrong-assumption.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I almost accepted that. But something feel wrong, and to find out I asked for the actual &lt;code class=&quot;language-text&quot;&gt;npm view&lt;/code&gt; output. That is when it fell apart:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular/core@17.3.2 peerDependencies
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rxjs: &lt;span class=&quot;token string&quot;&gt;&apos;^6.5.3 || ^7.4.0&apos;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&apos;zone.js&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~0.14.0&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular/core@18.2.13 peerDependencies
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rxjs: &lt;span class=&quot;token string&quot;&gt;&apos;^6.5.3 || ^7.4.0&apos;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&apos;zone.js&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~0.14.10&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular/core@19.2.21 peerDependencies
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rxjs: &lt;span class=&quot;token string&quot;&gt;&apos;^6.5.3 || ^7.4.0&apos;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&apos;zone.js&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~0.15.0&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular/core@20.3.19 peerDependencies
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rxjs: &lt;span class=&quot;token string&quot;&gt;&apos;^6.5.3 || ^7.4.0&apos;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&apos;zone.js&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~0.15.0&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a3fc591e9dbb5169015326301cfdf580/0f98f/02-rxjs-follow-up.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVMiA//xAAWEAADAAAAAAAAAAAAAAAAAAAAASD/2gAIAQEAAQUCHP8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAARAAESFB/9oACAEBAAE/IdqASuP/2gAMAwEAAgADAAAAEJAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxAAAQQDAAAAAAAAAAAAAAAAEQABEEEhUYH/2gAIAQEAAT8QZhucDaC32KT/AP/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;npm view output showing Angular 17 through 20 all accept RxJS 6 or 7&quot;
        title=&quot;npm view output showing Angular 17 through 20 all accept RxJS 6 or 7&quot;
        src=&quot;/static/a3fc591e9dbb5169015326301cfdf580/6a068/02-rxjs-follow-up.jpg&quot;
        srcset=&quot;/static/a3fc591e9dbb5169015326301cfdf580/09b79/02-rxjs-follow-up.jpg 240w,
/static/a3fc591e9dbb5169015326301cfdf580/7cc5e/02-rxjs-follow-up.jpg 480w,
/static/a3fc591e9dbb5169015326301cfdf580/6a068/02-rxjs-follow-up.jpg 960w,
/static/a3fc591e9dbb5169015326301cfdf580/644c5/02-rxjs-follow-up.jpg 1440w,
/static/a3fc591e9dbb5169015326301cfdf580/0f98f/02-rxjs-follow-up.jpg 1920w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Angular 17 through 20 all accept &lt;code class=&quot;language-text&quot;&gt;rxjs@^6.5.3 || ^7.4.0&lt;/code&gt;. &lt;code class=&quot;language-text&quot;&gt;rxjs@6.6.6&lt;/code&gt; is within that range. Claude &lt;strong&gt;made up&lt;/strong&gt; the Angular 18 requirement.&lt;/p&gt;
&lt;p&gt;RxJS 6 → 7 is not a blocker. Worth doing anyway since RxJS 6 has not had a release since 2021, &lt;code class=&quot;language-text&quot;&gt;.toPromise()&lt;/code&gt; is deprecated, and third-party Angular libs are dropping v6 support. But it does not gate the Angular upgrade.&lt;/p&gt;
&lt;p&gt;I also grepped &lt;code class=&quot;language-text&quot;&gt;apps/&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;libs/&lt;/code&gt; for &lt;code class=&quot;language-text&quot;&gt;.toPromise(&lt;/code&gt; and got zero hits. This PR ends up being just a version upgrade.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/79bf07884ccb212b6d6301bc522d1e7c/6dfd5/03-rxjs-correction.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeTNCGj/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAACAwAAAAAAAAAAAAAAAAAAAREgMf/aAAgBAQABPyF4RX//2gAMAwEAAgADAAAAEDAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRABAQEAAwAAAAAAAAAAAAAAAQAhEDFh/9oACAEBAAE/EAOEn1wM73IX/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Peer dependency analysis findings&quot;
        title=&quot;Peer dependency analysis findings&quot;
        src=&quot;/static/79bf07884ccb212b6d6301bc522d1e7c/6a068/03-rxjs-correction.jpg&quot;
        srcset=&quot;/static/79bf07884ccb212b6d6301bc522d1e7c/09b79/03-rxjs-correction.jpg 240w,
/static/79bf07884ccb212b6d6301bc522d1e7c/7cc5e/03-rxjs-correction.jpg 480w,
/static/79bf07884ccb212b6d6301bc522d1e7c/6a068/03-rxjs-correction.jpg 960w,
/static/79bf07884ccb212b6d6301bc522d1e7c/644c5/03-rxjs-correction.jpg 1440w,
/static/79bf07884ccb212b6d6301bc522d1e7c/0f98f/03-rxjs-correction.jpg 1920w,
/static/79bf07884ccb212b6d6301bc522d1e7c/6dfd5/03-rxjs-correction.jpg 3138w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I want to remember from this one.&lt;/strong&gt; Claude sounded sure. The confidence was not evidence. &lt;code class=&quot;language-text&quot;&gt;npm view&lt;/code&gt; took ten seconds and settled the question. Without pushing back, I would have shipped this PR based on a &lt;strong&gt;made-up requirement&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;34-cypress-non-blocker--pr-115&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#34-cypress-non-blocker--pr-115&quot; aria-label=&quot;34 cypress non blocker  pr 115 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.4 Cypress (non-blocker) · &lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/115&quot;&gt;PR #115&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view cypress@7.6.0 peerDependencies
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;none&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cypress has no peer deps. No Angular conflict.&lt;/p&gt;
&lt;p&gt;But I checked what the repo actually has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No &lt;code class=&quot;language-text&quot;&gt;apps/*-e2e&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;No &lt;code class=&quot;language-text&quot;&gt;cypress.config.ts&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;cypress.json&lt;/code&gt; outside &lt;code class=&quot;language-text&quot;&gt;node_modules/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;No &lt;code class=&quot;language-text&quot;&gt;*.cy.ts&lt;/code&gt; spec files anywhere in &lt;code class=&quot;language-text&quot;&gt;apps/&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;libs/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cypress&lt;/code&gt; is in &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;, downloaded on every &lt;code class=&quot;language-text&quot;&gt;yarn install&lt;/code&gt;, and nothing uses it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is not an upgrade. It is a removal. The plan was updated.&lt;/p&gt;
&lt;h2 id=&quot;4-four-subagents-one-parallel-run&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-four-subagents-one-parallel-run&quot; aria-label=&quot;4 four subagents one parallel run permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Four subagents, one parallel run&lt;/h2&gt;
&lt;p&gt;Two blockers, two non-blockers, nothing depends on anything else. Good fit for running all four in parallel.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b44f88850ef442b60cf19aa193b41a19/f4d02/04-subagents.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeWEQ2x//8QAFRABAQAAAAAAAAAAAAAAAAAAICH/2gAIAQEAAQUCi//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAMBAQAAAAAAAAAAAAAAAAABESEQ/9oACAEBAAE/IXgc49GiH//aAAwDAQACAAMAAAAQAA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAACAwADAAAAAAAAAAAAAAAAAREhMRBBUf/aAAgBAQABPxCCysfpFZx6CdGh/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Four subagents running in parallel&quot;
        title=&quot;Four subagents running in parallel&quot;
        src=&quot;/static/b44f88850ef442b60cf19aa193b41a19/6a068/04-subagents.jpg&quot;
        srcset=&quot;/static/b44f88850ef442b60cf19aa193b41a19/09b79/04-subagents.jpg 240w,
/static/b44f88850ef442b60cf19aa193b41a19/7cc5e/04-subagents.jpg 480w,
/static/b44f88850ef442b60cf19aa193b41a19/6a068/04-subagents.jpg 960w,
/static/b44f88850ef442b60cf19aa193b41a19/644c5/04-subagents.jpg 1440w,
/static/b44f88850ef442b60cf19aa193b41a19/0f98f/04-subagents.jpg 1920w,
/static/b44f88850ef442b60cf19aa193b41a19/f4d02/04-subagents.jpg 2387w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Commit the plan and briefs to &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; first. Every subagent’s worktree starts from the same docs.&lt;/li&gt;
&lt;li&gt;Spawn four Claude Code subagents in parallel, one per PR, each in its own git worktree so they do not step on each other.&lt;/li&gt;
&lt;li&gt;Each subagent reads its brief, executes, validates, and commits on its branch. No pushes, no PRs. I review before merging.&lt;/li&gt;
&lt;li&gt;Merge in order: 2.1 → 2.2 → 2.3 → 2.4. Blockers first. &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;yarn.lock&lt;/code&gt; conflicts are expected, so each PR rebases onto &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; after the previous one lands.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The briefs are at &lt;code class=&quot;language-text&quot;&gt;docs/plans/prep/2.1-sentry-v8.md&lt;/code&gt; through &lt;code class=&quot;language-text&quot;&gt;2.4-cypress-remove.md&lt;/code&gt;. Each one is self-contained with the scope, current state, exact commands, and a validation checklist. A subagent can start cold without reading anything else.&lt;/p&gt;
&lt;p&gt;In the Jira Clone series I ran everything sequentially in one Claude Code session. Here, four sessions run on four branches at the same time.&lt;/p&gt;
&lt;h2 id=&quot;5-source&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-source&quot; aria-label=&quot;5 source permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Source&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Plan: &lt;a href=&quot;https://github.com/trungvose/angular-spotify/blob/main/docs/plans/2026-04-18-angular-20-migration-plan.md&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docs/plans/2026-04-18-angular-20-migration-plan.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Briefs: &lt;a href=&quot;https://github.com/trungvose/angular-spotify/tree/main/docs/plans/prep&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docs/plans/prep/&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href=&quot;https://github.com/trungvose/angular-spotify&quot;&gt;https://github.com/trungvose/angular-spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Jira Clone counterpart: &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part7-angular-20/&quot;&gt;Part 7, Angular 20 with Claude Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;6-two-lessons&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-two-lessons&quot; aria-label=&quot;6 two lessons permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. Two lessons&lt;/h2&gt;
&lt;p&gt;Both cases had the same pattern: a confident answer, close to right but not quite, with a plausible-looking source. Both only surfaced because I pushed back.&lt;/p&gt;
&lt;p&gt;Three things I am keeping from this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code class=&quot;language-text&quot;&gt;npm view &amp;lt;pkg&gt;@&amp;lt;version&gt; peerDependencies&lt;/code&gt; yourself occasionally. Do not accept “requires X” without verifying.&lt;/li&gt;
&lt;li&gt;When an AI cites a URL, open it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is not an argument against using Claude Code for migrations. Both mistakes were caught in the back-and-forth. The problem is not that the AI makes things up. It is that you accept them without checking.&lt;/p&gt;
&lt;p&gt;Pushing back matters more as AI gets better at sounding confident. I wish AI would push back &lt;strong&gt;more often&lt;/strong&gt; 😂&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to hide Agent View by default on startup in Antigravity]]></title><link>https://trungvose.comhide-agent-view-antigravity/</link><guid isPermaLink="false">https://trungvose.comhide-agent-view-antigravity/</guid><pubDate>Sat, 11 Apr 2026 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I really enjoy using &lt;a href=&quot;https://antigravity.google/&quot;&gt;Antigravity&lt;/a&gt; so far. I use it mainly as an editor like VS Code, but it has some nice touches. For example, the Save prompt when closing an unsaved file has keyboard-navigable options that you can cycle through without reaching for the mouse. Even though you can always use &lt;code class=&quot;language-text&quot;&gt;Cmd + S&lt;/code&gt; to save or &lt;code class=&quot;language-text&quot;&gt;Cmd + D&lt;/code&gt; to discard in VS Code since those are built-in Mac shortcuts, it feels very nice to have full keyboard navigation on the dialog itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/dd14137ef01c1fb2de679a707773e84d/00-antigravity-prompt-save.gif&quot; alt=&quot;Antigravity Save prompt with keyboard-navigable options&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;That said, since I use Claude Code as my primary agent, I don’t need the Agent view that Antigravity opens by default on startup.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/79c9b9c3e6a762160bfd7ac39f9b938d/e3def/00-antigravity-agent-panel.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABYBAQEBAAAAAAAAAAAAAAAAAAABA//aAAwDAQACEAMQAAAByWmWUTX/xAAWEAEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQEAAQUCIzbIIL//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAwEBPwFH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFxAAAwEAAAAAAAAAAAAAAAAAABEhIP/aAAgBAQAGPwIrRMf/xAAaEAEAAgMBAAAAAAAAAAAAAAABABEhQWEQ/9oACAEBAAE/IabWMLJw1LFcw634Fh1uf//aAAwDAQACAAMAAAAQfw//xAAWEQEBAQAAAAAAAAAAAAAAAAABACH/2gAIAQMBAT8QCGX/xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQIBAT8QxY//xAAaEAEBAQEBAQEAAAAAAAAAAAABEQAhMXGR/9oACAEBAAE/EESnWkC7zpudV+x1ALeF37zJOVwDVwn31v/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Antigravity Agent panel open on startup&quot;
        title=&quot;Antigravity Agent panel open on startup&quot;
        src=&quot;/static/79c9b9c3e6a762160bfd7ac39f9b938d/6a068/00-antigravity-agent-panel.jpg&quot;
        srcset=&quot;/static/79c9b9c3e6a762160bfd7ac39f9b938d/09b79/00-antigravity-agent-panel.jpg 240w,
/static/79c9b9c3e6a762160bfd7ac39f9b938d/7cc5e/00-antigravity-agent-panel.jpg 480w,
/static/79c9b9c3e6a762160bfd7ac39f9b938d/6a068/00-antigravity-agent-panel.jpg 960w,
/static/79c9b9c3e6a762160bfd7ac39f9b938d/644c5/00-antigravity-agent-panel.jpg 1440w,
/static/79c9b9c3e6a762160bfd7ac39f9b938d/0f98f/00-antigravity-agent-panel.jpg 1920w,
/static/79c9b9c3e6a762160bfd7ac39f9b938d/e3def/00-antigravity-agent-panel.jpg 2800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here is how to turn it off.&lt;/p&gt;
&lt;h2 id=&quot;steps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#steps&quot; aria-label=&quot;steps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Click on the gear/settings icon in the header at the top-right corner, then click on “Open Antigravity User Settings”. A dialog will open.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/faf8e707066d0c8f9848453598fe1276/b4de7/01-gravity-user-setting.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHJTdSWR//EABcQAQEBAQAAAAAAAAAAAAAAAAARARD/2gAIAQEAAQUCzsRH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBABAQEBAQAAAAAAAAAAAAAAAQARIUH/2gAIAQEAAT8hRnVvXFunswNm/9oADAMBAAIAAwAAABBgP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EIj/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxCI/8QAGxABAAMAAwEAAAAAAAAAAAAAAQARITFBYYH/2gAIAQEAAT8QDY62ZsvCtXLBys/YIVbGds9Gf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Opening Antigravity User Settings from the gear icon&quot;
        title=&quot;Opening Antigravity User Settings from the gear icon&quot;
        src=&quot;/static/faf8e707066d0c8f9848453598fe1276/6a068/01-gravity-user-setting.jpg&quot;
        srcset=&quot;/static/faf8e707066d0c8f9848453598fe1276/09b79/01-gravity-user-setting.jpg 240w,
/static/faf8e707066d0c8f9848453598fe1276/7cc5e/01-gravity-user-setting.jpg 480w,
/static/faf8e707066d0c8f9848453598fe1276/6a068/01-gravity-user-setting.jpg 960w,
/static/faf8e707066d0c8f9848453598fe1276/644c5/01-gravity-user-setting.jpg 1440w,
/static/faf8e707066d0c8f9848453598fe1276/0f98f/01-gravity-user-setting.jpg 1920w,
/static/faf8e707066d0c8f9848453598fe1276/b4de7/01-gravity-user-setting.jpg 2740w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Under the “Agent” section, scroll down and find &lt;code class=&quot;language-text&quot;&gt;Open Agent on Reload&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d4643a8135aa9d115be8865e33626270/df51d/02-turn-on-by-default.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeO0Sof/xAAXEAADAQAAAAAAAAAAAAAAAAAAEEER/9oACAEBAAEFAqqYf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAAAABESAxQf/aAAgBAQABPyGg9FLp/9oADAMBAAIAAwAAABDDD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB4QAAIBAwUAAAAAAAAAAAAAAAABIRARMUFRgZHB/9oACAEBAAE/ENHg8y30PLHHOg69wnY//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Open Agent on Reload setting under the Agent section&quot;
        title=&quot;Open Agent on Reload setting under the Agent section&quot;
        src=&quot;/static/d4643a8135aa9d115be8865e33626270/6a068/02-turn-on-by-default.jpg&quot;
        srcset=&quot;/static/d4643a8135aa9d115be8865e33626270/09b79/02-turn-on-by-default.jpg 240w,
/static/d4643a8135aa9d115be8865e33626270/7cc5e/02-turn-on-by-default.jpg 480w,
/static/d4643a8135aa9d115be8865e33626270/6a068/02-turn-on-by-default.jpg 960w,
/static/d4643a8135aa9d115be8865e33626270/644c5/02-turn-on-by-default.jpg 1440w,
/static/d4643a8135aa9d115be8865e33626270/0f98f/02-turn-on-by-default.jpg 1920w,
/static/d4643a8135aa9d115be8865e33626270/df51d/02-turn-on-by-default.jpg 2400w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Turn it off. By default, it is turned on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e9080d3981ab517f2a8405578e588ad1/df51d/03-turn-off.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeO0Ssf/xAAXEAADAQAAAAAAAAAAAAAAAAAAEEEx/9oACAEBAAEFAqqaf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAMBAQAAAAAAAAAAAAAAAAABMSEg/9oACAEBAAE/IeB02nMP/9oADAMBAAIAAwAAABDAz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB0QAAICAQUAAAAAAAAAAAAAAAABITEQQVGBkcH/2gAIAQEAAT8Q0eDuW+hy2OOeBX7JCo//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Turning off the Open Agent on Reload toggle&quot;
        title=&quot;Turning off the Open Agent on Reload toggle&quot;
        src=&quot;/static/e9080d3981ab517f2a8405578e588ad1/6a068/03-turn-off.jpg&quot;
        srcset=&quot;/static/e9080d3981ab517f2a8405578e588ad1/09b79/03-turn-off.jpg 240w,
/static/e9080d3981ab517f2a8405578e588ad1/7cc5e/03-turn-off.jpg 480w,
/static/e9080d3981ab517f2a8405578e588ad1/6a068/03-turn-off.jpg 960w,
/static/e9080d3981ab517f2a8405578e588ad1/644c5/03-turn-off.jpg 1440w,
/static/e9080d3981ab517f2a8405578e588ad1/0f98f/03-turn-off.jpg 1920w,
/static/e9080d3981ab517f2a8405578e588ad1/df51d/03-turn-off.jpg 2400w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That is it. Next time you open Antigravity, the Agent view will no longer appear on startup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/4ac3e8c0bf09b6ed016fb58e7660398c/04-final-result.gif&quot; alt=&quot;Antigravity starting up without the Agent view&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrade to Angular 20 from Angular 13 - Part 7: Angular 20 with Claude Code]]></title><link>https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part7-angular-20/</link><guid isPermaLink="false">https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part7-angular-20/</guid><pubDate>Fri, 03 Apr 2026 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We made it. In &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part6-angular-19/&quot;&gt;Part 6&lt;/a&gt;, we upgraded &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; from Angular 18 to 19. That was the quietest upgrade in the series: remove &lt;code class=&quot;language-text&quot;&gt;standalone: true&lt;/code&gt;, bump some versions, done.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5fa7a4cf0b5d4a891c635ffff6a80789/48638/01-prompt.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVFYSD/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAEAAwEAAAAAAAAAAAAAAAABABAxYf/aAAgBAQABPyF7Epwjlf/aAAwDAQACAAMAAAAQ4w//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAACAgMBAAAAAAAAAAAAAAAAARExECGBof/aAAgBAQABPxBpSaToZcI4eIsx/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude Code prompt for Angular 20 upgrade&quot;
        title=&quot;Claude Code prompt for Angular 20 upgrade&quot;
        src=&quot;/static/5fa7a4cf0b5d4a891c635ffff6a80789/6a068/01-prompt.jpg&quot;
        srcset=&quot;/static/5fa7a4cf0b5d4a891c635ffff6a80789/09b79/01-prompt.jpg 240w,
/static/5fa7a4cf0b5d4a891c635ffff6a80789/7cc5e/01-prompt.jpg 480w,
/static/5fa7a4cf0b5d4a891c635ffff6a80789/6a068/01-prompt.jpg 960w,
/static/5fa7a4cf0b5d4a891c635ffff6a80789/644c5/01-prompt.jpg 1440w,
/static/5fa7a4cf0b5d4a891c635ffff6a80789/48638/01-prompt.jpg 1827w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Part 7 is the opposite. This is the biggest single upgrade in the entire series, and also the longest Claude Code session at &lt;code class=&quot;language-text&quot;&gt;36 minutes&lt;/code&gt;. Not because Angular 20 itself is hard, but because we took the opportunity to modernize everything at once: the build system, the test runner, the linter config, and even cleaned out Storybook.&lt;/p&gt;
&lt;h2 id=&quot;1-what-changed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-what-changed&quot; aria-label=&quot;1 what changed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. What changed&lt;/h2&gt;
&lt;p&gt;Here is everything we did in one branch:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Angular 19 to 20&lt;/strong&gt; with all ecosystem deps (ng-zorro-antd 20, ngx-quill 28, CDK 20)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build system migration&lt;/strong&gt;: dropped &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt; (webpack) and moved to &lt;code class=&quot;language-text&quot;&gt;@angular/build:application&lt;/code&gt; (esbuild)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TailwindCSS v4 attempt and revert&lt;/strong&gt;: tried migrating to v4, hit critical incompatibilities with Angular component styles, reverted to v3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Karma to Vitest&lt;/strong&gt;: removed Karma + Jasmine, migrated all spec files to Vitest&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ESLint flat config&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;eslint.config.js&lt;/code&gt; with ESLint 9&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storybook removal&lt;/strong&gt;: deleted Storybook v6 and all related deps&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deprecated deps cleanup&lt;/strong&gt;: removed codelyzer, tslint, nz-tslint-rules, protractor&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a12fbdcba0ab382271b9772b8fd5ad2/b17f8/02-what-change.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGQAAAQUAAAAAAAAAAAAAAAAAAAECAwQF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAdd0gilcr//EABkQAAIDAQAAAAAAAAAAAAAAAAECABARQf/aAAgBAQABBQK8nFckf//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AVf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwFH/8QAFhAAAwAAAAAAAAAAAAAAAAAAACBB/9oACAEBAAY/Alh//8QAGhAAAgMBAQAAAAAAAAAAAAAAAAERUaEhMf/aAAgBAQABPyFN3om70XhChqGO0wf/2gAMAwEAAgADAAAAEMwP/8QAFREBAQAAAAAAAAAAAAAAAAAAAQD/2gAIAQMBAT8QgN//xAAVEQEBAAAAAAAAAAAAAAAAAAABAP/aAAgBAgEBPxCUX//EABoQAQADAAMAAAAAAAAAAAAAAAEAESExQdH/2gAIAQEAAT8Q1F4tFJjglfSEgCwUid3GsXs//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;What changed in Angular 20 upgrade&quot;
        title=&quot;What changed in Angular 20 upgrade&quot;
        src=&quot;/static/1a12fbdcba0ab382271b9772b8fd5ad2/6a068/02-what-change.jpg&quot;
        srcset=&quot;/static/1a12fbdcba0ab382271b9772b8fd5ad2/09b79/02-what-change.jpg 240w,
/static/1a12fbdcba0ab382271b9772b8fd5ad2/7cc5e/02-what-change.jpg 480w,
/static/1a12fbdcba0ab382271b9772b8fd5ad2/6a068/02-what-change.jpg 960w,
/static/1a12fbdcba0ab382271b9772b8fd5ad2/644c5/02-what-change.jpg 1440w,
/static/1a12fbdcba0ab382271b9772b8fd5ad2/b17f8/02-what-change.jpg 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-dependency-upgrades&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-dependency-upgrades&quot; aria-label=&quot;2 dependency upgrades permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Dependency upgrades&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Before (v19)&lt;/th&gt;
&lt;th&gt;After (v20)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.2.20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^20.3.18&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.2.22&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^20.3.21&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.2.19&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^20.2.14&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.3.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^20.4.4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@ant-design/icons-angular&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^20.0.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^27.1.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^28.x&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-eslint/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;19.8.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;20.7.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;eslint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^8.57.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^9.28.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;tailwindcss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^3.0.12&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^3.4.17&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;typescript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~5.8.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~5.8.3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A lot of packages were added (Vitest, &lt;code class=&quot;language-text&quot;&gt;@angular/build&lt;/code&gt;) and even more were removed (Karma, Storybook, webpack tooling, deprecated linters). The &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; got noticeably lighter.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2a5dfd1ea30981463e6f37d092764c14/b17f8/03-big-bang.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAASABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB7F87poFgJIX/xAAVEAEBAAAAAAAAAAAAAAAAAAAgEf/aAAgBAQABBQIU/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAY/Ah//xAAbEAEBAAEFAAAAAAAAAAAAAAABADERIFGB8P/aAAgBAQABPyEXlu4xEeaRjZ//2gAMAwEAAgADAAAAEOwAwP/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8QH//EABoQAQACAwEAAAAAAAAAAAAAAAEAIRAxYVH/2gAIAQEAAT8QZw7Bq40RI1U9FjRAAoMGp//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Big bang upgrade approach&quot;
        title=&quot;Big bang upgrade approach&quot;
        src=&quot;/static/2a5dfd1ea30981463e6f37d092764c14/6a068/03-big-bang.jpg&quot;
        srcset=&quot;/static/2a5dfd1ea30981463e6f37d092764c14/09b79/03-big-bang.jpg 240w,
/static/2a5dfd1ea30981463e6f37d092764c14/7cc5e/03-big-bang.jpg 480w,
/static/2a5dfd1ea30981463e6f37d092764c14/6a068/03-big-bang.jpg 960w,
/static/2a5dfd1ea30981463e6f37d092764c14/644c5/03-big-bang.jpg 1440w,
/static/2a5dfd1ea30981463e6f37d092764c14/b17f8/03-big-bang.jpg 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;3-build-system-and-tailwindcss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-build-system-and-tailwindcss&quot; aria-label=&quot;3 build system and tailwindcss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Build system and TailwindCSS&lt;/h2&gt;
&lt;h3 id=&quot;31-esbuild-migration-success&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31-esbuild-migration-success&quot; aria-label=&quot;31 esbuild migration success permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1 Esbuild migration (success)&lt;/h3&gt;
&lt;p&gt;The project has used &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt; since Angular 13 purely for TailwindCSS PostCSS processing. That is a lot of webpack baggage for one CSS preprocessor.&lt;/p&gt;
&lt;p&gt;Angular 20 with the &lt;code class=&quot;language-text&quot;&gt;application&lt;/code&gt; builder (esbuild) handles PostCSS natively. So the entire custom webpack setup becomes unnecessary. We dropped &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt;, deleted &lt;code class=&quot;language-text&quot;&gt;webpack.config.js&lt;/code&gt;, and pointed &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt; at &lt;code class=&quot;language-text&quot;&gt;@angular/build:application&lt;/code&gt;. Build times improved noticeably with esbuild, though I did not benchmark it.&lt;/p&gt;
&lt;p&gt;That part went fine. The TailwindCSS part did not.&lt;/p&gt;
&lt;h3 id=&quot;32-tailwind-v4-attempt-failure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#32-tailwind-v4-attempt-failure&quot; aria-label=&quot;32 tailwind v4 attempt failure permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.2 Tailwind v4 attempt (failure)&lt;/h3&gt;
&lt;p&gt;Claude suggested upgrading TailwindCSS from v3 to v4 as part of the modernization. The agent converted the 500-line &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; into CSS &lt;code class=&quot;language-text&quot;&gt;@theme&lt;/code&gt; blocks inside &lt;code class=&quot;language-text&quot;&gt;styles.scss&lt;/code&gt;, changed the import to &lt;code class=&quot;language-text&quot;&gt;@import &apos;tailwindcss&apos;&lt;/code&gt;, and deleted the old config file. The build passed. Tests passed. Everything looked fine in the terminal.&lt;/p&gt;
&lt;p&gt;Then I opened &lt;code class=&quot;language-text&quot;&gt;localhost:4200&lt;/code&gt;. The entire UI was broken. No layout, no colors, no spacing. The Kanban board was just raw text floating on a white page.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f4b8a5bee8536e39ef81f2e35defc172/8ec0a/04-tailwind-v4.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHtaxYhh//EABkQAAMAAwAAAAAAAAAAAAAAAAECEAARMf/aAAgBAQABBQJAZrF5P//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAQEBAAAAAAAAAAAAAAAAABABIv/aAAgBAQAGPwLTH//EABsQAAICAwEAAAAAAAAAAAAAAAERACEQMVFh/9oACAEBAAE/IUSSBZqohyJyb3mf/9oADAMBAAIAAwAAABCzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAAIDAQEAAAAAAAAAAAAAAAABESFhEDH/2gAIAQEAAT8QStORhCMhiLOetO//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Broken UI after Tailwind v4 migration&quot;
        title=&quot;Broken UI after Tailwind v4 migration&quot;
        src=&quot;/static/f4b8a5bee8536e39ef81f2e35defc172/6a068/04-tailwind-v4.jpg&quot;
        srcset=&quot;/static/f4b8a5bee8536e39ef81f2e35defc172/09b79/04-tailwind-v4.jpg 240w,
/static/f4b8a5bee8536e39ef81f2e35defc172/7cc5e/04-tailwind-v4.jpg 480w,
/static/f4b8a5bee8536e39ef81f2e35defc172/6a068/04-tailwind-v4.jpg 960w,
/static/f4b8a5bee8536e39ef81f2e35defc172/644c5/04-tailwind-v4.jpg 1440w,
/static/f4b8a5bee8536e39ef81f2e35defc172/0f98f/04-tailwind-v4.jpg 1920w,
/static/f4b8a5bee8536e39ef81f2e35defc172/8ec0a/04-tailwind-v4.jpg 2560w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;33-investigating-tailwind-v4&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#33-investigating-tailwind-v4&quot; aria-label=&quot;33 investigating tailwind v4 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.3 Investigating Tailwind v4&lt;/h3&gt;
&lt;p&gt;Working with Claude interactively this time, we looked into why things broke. We connected to the running app via Chrome DevTools and found multiple issues stacked on top of each other:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem 1: Sass ate the Tailwind import.&lt;/strong&gt; The global stylesheet was &lt;code class=&quot;language-text&quot;&gt;styles.scss&lt;/code&gt;. Sass runs before PostCSS, so it resolved &lt;code class=&quot;language-text&quot;&gt;@import &apos;tailwindcss&apos;&lt;/code&gt; by inlining the raw CSS file from &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt;. By the time Tailwind’s PostCSS plugin ran, there was nothing left to process. Zero utility classes generated. Fix: rename &lt;code class=&quot;language-text&quot;&gt;styles.scss&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;styles.css&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9dec6cb9f466c4a50c980ae52df843b7/b17f8/05-postcss-issue.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe1FDY//xAAWEAADAAAAAAAAAAAAAAAAAAABECD/2gAIAQEAAQUCQj//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAIEH/2gAIAQEABj8CKv8A/8QAGhAAAgIDAAAAAAAAAAAAAAAAAAEhYTFBkf/aAAgBAQABPyHoxhsmiaEoP//aAAwDAQACAAMAAAAQ6y//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAABBAIDAAAAAAAAAAAAAAABABEhMUFRcZHx/9oACAEBAAE/EAZM20mPlx6gFAE5TgTFtIEsdEBi/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sass eating the Tailwind import&quot;
        title=&quot;Sass eating the Tailwind import&quot;
        src=&quot;/static/9dec6cb9f466c4a50c980ae52df843b7/6a068/05-postcss-issue.jpg&quot;
        srcset=&quot;/static/9dec6cb9f466c4a50c980ae52df843b7/09b79/05-postcss-issue.jpg 240w,
/static/9dec6cb9f466c4a50c980ae52df843b7/7cc5e/05-postcss-issue.jpg 480w,
/static/9dec6cb9f466c4a50c980ae52df843b7/6a068/05-postcss-issue.jpg 960w,
/static/9dec6cb9f466c4a50c980ae52df843b7/644c5/05-postcss-issue.jpg 1440w,
/static/9dec6cb9f466c4a50c980ae52df843b7/b17f8/05-postcss-issue.jpg 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem 2: Missing PostCSS plugin.&lt;/strong&gt; Angular’s &lt;code class=&quot;language-text&quot;&gt;@angular/build&lt;/code&gt; needed &lt;code class=&quot;language-text&quot;&gt;@tailwindcss/postcss&lt;/code&gt; registered in a &lt;code class=&quot;language-text&quot;&gt;postcss.config.json&lt;/code&gt; file. Not &lt;code class=&quot;language-text&quot;&gt;.js&lt;/code&gt;, not &lt;code class=&quot;language-text&quot;&gt;.mjs&lt;/code&gt;. Angular only reads the JSON format.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f79870af9316d6752064682c7999f0c8/b17f8/06-postcss-config.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHt2DQT/8QAFhABAQEAAAAAAAAAAAAAAAAAABEg/9oACAEBAAEFAsRH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAHBAAAgEFAQAAAAAAAAAAAAAAABEBECFRofBh/9oACAEBAAE/IVyF5qinOjliII//2gAMAwEAAgADAAAAEIs//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQADAAMBAAAAAAAAAAAAAAEAETEhQVGR/9oACAEBAAE/EKK2G+oAwfUM7lWCLOowQcT/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PostCSS config for Angular&quot;
        title=&quot;PostCSS config for Angular&quot;
        src=&quot;/static/f79870af9316d6752064682c7999f0c8/6a068/06-postcss-config.jpg&quot;
        srcset=&quot;/static/f79870af9316d6752064682c7999f0c8/09b79/06-postcss-config.jpg 240w,
/static/f79870af9316d6752064682c7999f0c8/7cc5e/06-postcss-config.jpg 480w,
/static/f79870af9316d6752064682c7999f0c8/6a068/06-postcss-config.jpg 960w,
/static/f79870af9316d6752064682c7999f0c8/644c5/06-postcss-config.jpg 1440w,
/static/f79870af9316d6752064682c7999f0c8/b17f8/06-postcss-config.jpg 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem 3: Component styles need &lt;code class=&quot;language-text&quot;&gt;@reference&lt;/code&gt;.&lt;/strong&gt; In Tailwind v4, &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; no longer works globally. Angular component SCSS files that use &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; cannot access the global theme. Each component file needs &lt;code class=&quot;language-text&quot;&gt;@reference &quot;path/to/styles.css&quot;&lt;/code&gt; at the top, with the correct relative path. We have 15+ component files at different directory depths. I tried building a custom PostCSS plugin to auto-inject the reference, but Angular’s plugin loader made that complicated too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem 4: It is a known issue.&lt;/strong&gt; I found &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/discussions/17416&quot;&gt;this GitHub discussion&lt;/a&gt; confirming &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;@reference&lt;/code&gt; does not work well in Angular. People report dev server startup times going from 38 seconds to 3+ minutes. There is no official solution from either the Tailwind or Angular team.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/35f1c64d04aeb36b0b4c0eb0dec01604/d93a2/07-tailwind-apply.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.16666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAdoJQP/EABUQAQEAAAAAAAAAAAAAAAAAABEg/9oACAEBAAEFAmv/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAADAQEAAAAAAAAAAAAAAAAAAREQMf/aAAgBAQABPyEViurh/9oADAMBAAIAAwAAABBMP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAAICAwEAAAAAAAAAAAAAAAERACEQQZFx/9oACAEBAAE/EBTfDA6q9BhJa5EM3//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Tailwind v4 apply with reference issue&quot;
        title=&quot;Tailwind v4 apply with reference issue&quot;
        src=&quot;/static/35f1c64d04aeb36b0b4c0eb0dec01604/6a068/07-tailwind-apply.jpg&quot;
        srcset=&quot;/static/35f1c64d04aeb36b0b4c0eb0dec01604/09b79/07-tailwind-apply.jpg 240w,
/static/35f1c64d04aeb36b0b4c0eb0dec01604/7cc5e/07-tailwind-apply.jpg 480w,
/static/35f1c64d04aeb36b0b4c0eb0dec01604/6a068/07-tailwind-apply.jpg 960w,
/static/35f1c64d04aeb36b0b4c0eb0dec01604/644c5/07-tailwind-apply.jpg 1440w,
/static/35f1c64d04aeb36b0b4c0eb0dec01604/d93a2/07-tailwind-apply.jpg 1868w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;34-downgrading-to-tailwind-v3&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#34-downgrading-to-tailwind-v3&quot; aria-label=&quot;34 downgrading to tailwind v3 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.4 Downgrading to Tailwind v3&lt;/h3&gt;
&lt;p&gt;After all that, we downgraded to Tailwind v3 (&lt;code class=&quot;language-text&quot;&gt;^3.4.17&lt;/code&gt;). Restored the old &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; from git. Restored &lt;code class=&quot;language-text&quot;&gt;styles.scss&lt;/code&gt; with v3 directives (&lt;code class=&quot;language-text&quot;&gt;@tailwind base&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@tailwind components&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@tailwind utilities&lt;/code&gt;). Removed all the v4 PostCSS config. Everything just worked.&lt;/p&gt;
&lt;p&gt;In v3, &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; is global by default. No &lt;code class=&quot;language-text&quot;&gt;@reference&lt;/code&gt; needed. No custom PostCSS plugins. I updated the &lt;code class=&quot;language-text&quot;&gt;purge&lt;/code&gt; field to &lt;code class=&quot;language-text&quot;&gt;content&lt;/code&gt; for current v3 compatibility, and that was it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/5fba2bd002090d9c206b5fe7644899ca/08-fixing-tailwind-issue.gif&quot; alt=&quot;Fixing Tailwind issue and downgrading to v3&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;We also fixed a few more CSS issues while we were in there. The &lt;code class=&quot;language-text&quot;&gt;nz-select&lt;/code&gt; custom templates in the create issue modal had a 60px height bug. ng-zorro adds a &lt;code class=&quot;language-text&quot;&gt;::after&lt;/code&gt; strut pseudo-element to &lt;code class=&quot;language-text&quot;&gt;.ant-select-selection-item&lt;/code&gt; that was stacking with the custom template content (30px strut + 30px content = 60px). We tracked this down through Chrome DevTools and fixed it with a &lt;code class=&quot;language-text&quot;&gt;display: flex&lt;/code&gt; override.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/72e0a234a195f38b0bd517ee323182da/8ec0a/09-ng-zorro.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.16666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAASABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAQBAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABsq9cIlw60jAf/8QAGhAAAgMBAQAAAAAAAAAAAAAAAQIAEBIDEf/aAAgBAQABBQLhp0wYjEg15DDX/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAGhAAAgIDAAAAAAAAAAAAAAAAADEBAhEgkf/aAAgBAQAGPwLM2kdujFt//8QAHBAAAgICAwAAAAAAAAAAAAAAAAEhMRARQVHx/9oACAEBAAE/IZIXHJ4QZG2sRJRoaihJ0sKY/9oADAMBAAIAAwAAABAzyP7/xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAwEBPxAh/8QAFREBAQAAAAAAAAAAAAAAAAAAESD/2gAIAQIBAT8QY//EABwQAQACAwEBAQAAAAAAAAAAAAEAESExcUEQ4f/aAAgBAQABPxCvBS1E2X7E7ayt/hFA3DPCCPd4BGmVOypguE0O/A1P/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Fixing ng-zorro select height bug&quot;
        title=&quot;Fixing ng-zorro select height bug&quot;
        src=&quot;/static/72e0a234a195f38b0bd517ee323182da/6a068/09-ng-zorro.jpg&quot;
        srcset=&quot;/static/72e0a234a195f38b0bd517ee323182da/09b79/09-ng-zorro.jpg 240w,
/static/72e0a234a195f38b0bd517ee323182da/7cc5e/09-ng-zorro.jpg 480w,
/static/72e0a234a195f38b0bd517ee323182da/6a068/09-ng-zorro.jpg 960w,
/static/72e0a234a195f38b0bd517ee323182da/644c5/09-ng-zorro.jpg 1440w,
/static/72e0a234a195f38b0bd517ee323182da/0f98f/09-ng-zorro.jpg 1920w,
/static/72e0a234a195f38b0bd517ee323182da/8ec0a/09-ng-zorro.jpg 2560w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-karma-to-vitest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-karma-to-vitest&quot; aria-label=&quot;4 karma to vitest permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Karma to Vitest&lt;/h2&gt;
&lt;p&gt;Karma is deprecated in Angular 20 and scheduled for removal in v22. We moved to Vitest now rather than waiting.&lt;/p&gt;
&lt;p&gt;The migration was more involved than expected. Vitest uses &lt;code class=&quot;language-text&quot;&gt;vi.fn()&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;jasmine.createSpy()&lt;/code&gt;, and the spy API is different enough that every spec file with mocks needed updating:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Jasmine&lt;/th&gt;
&lt;th&gt;Vitest&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;jasmine.createSpy(&apos;name&apos;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;vi.fn()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;spy.and.returnValue(x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;spy.mockReturnValue(x)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;spy.and.callFake(fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;spy.mockImplementation(fn)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;spy.calls.reset()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;spy.mockReset()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;spyOn(obj, &apos;method&apos;).and.callThrough()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;vi.spyOn(obj, &apos;method&apos;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;About half the spec files needed these conversions. The rest used only &lt;code class=&quot;language-text&quot;&gt;describe&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;it&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;expect&lt;/code&gt; which work identically in Vitest. All tests passing after the migration.&lt;/p&gt;
&lt;h2 id=&quot;5-eslint-flat-config&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-eslint-flat-config&quot; aria-label=&quot;5 eslint flat config permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. ESLint flat config&lt;/h2&gt;
&lt;p&gt;ESLint 9 uses a new &lt;code class=&quot;language-text&quot;&gt;eslint.config.js&lt;/code&gt; format (flat config) instead of the old &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt;. The legacy format still works in ESLint 9 but will be removed in ESLint 10.&lt;/p&gt;
&lt;p&gt;The migration preserved all our existing rules: naming conventions, disabled rules, component selector configs. The flat config format is actually cleaner. No more &lt;code class=&quot;language-text&quot;&gt;overrides&lt;/code&gt; with file globs nested inside. Everything is top-level.&lt;/p&gt;
&lt;p&gt;We also removed three deprecated packages that have been sitting in &lt;code class=&quot;language-text&quot;&gt;devDependencies&lt;/code&gt; since the Angular 13 days: &lt;code class=&quot;language-text&quot;&gt;codelyzer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;tslint&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;nz-tslint-rules&lt;/code&gt;. They were not doing anything.&lt;/p&gt;
&lt;h2 id=&quot;6-storybook-removal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-storybook-removal&quot; aria-label=&quot;6 storybook removal permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. Storybook removal&lt;/h2&gt;
&lt;p&gt;Storybook v6 has been skipped across four consecutive migrations (Parts 4, 5, 6, and now 7). It stopped working properly after the Angular 17 upgrade and we kept carrying it forward. Time to let go.&lt;/p&gt;
&lt;p&gt;Removing Storybook and its transitive dependencies made a surprising reduction in the dependency tree. If we need component documentation in the future, starting fresh with Storybook v8 will be cleaner than upgrading from v6.&lt;/p&gt;
&lt;h2 id=&quot;7-claude-code-session&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-claude-code-session&quot; aria-label=&quot;7 claude code session permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. Claude Code session&lt;/h2&gt;
&lt;p&gt;This was the longest single Claude Code session in the series at 36 minutes. For context, the Angular 19 upgrade (Part 6) took about 15 minutes.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7114552cf73676468b46501899694cf9/9238c/10-claude-session.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.25000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHJq+BA0P/EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAMAAwEAAAAAAAAAAAAAAAABERAhUUH/2gAIAQEAAT8h8HiKLRFxEXD/2gAMAwEAAgADAAAAEIAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAAIBBQAAAAAAAAAAAAAAAQAxURARQZHx/9oACAEBAAE/EEEVXLABWi24KxPMjgdT/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude Code session overview&quot;
        title=&quot;Claude Code session overview&quot;
        src=&quot;/static/7114552cf73676468b46501899694cf9/6a068/10-claude-session.jpg&quot;
        srcset=&quot;/static/7114552cf73676468b46501899694cf9/09b79/10-claude-session.jpg 240w,
/static/7114552cf73676468b46501899694cf9/7cc5e/10-claude-session.jpg 480w,
/static/7114552cf73676468b46501899694cf9/6a068/10-claude-session.jpg 960w,
/static/7114552cf73676468b46501899694cf9/644c5/10-claude-session.jpg 1440w,
/static/7114552cf73676468b46501899694cf9/0f98f/10-claude-session.jpg 1920w,
/static/7114552cf73676468b46501899694cf9/9238c/10-claude-session.jpg 2970w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The extra time was expected. This session covered four major migrations (build, tests, linter, cleanup) on top of the standard version bump.&lt;/p&gt;
&lt;p&gt;However, the automated session &lt;code class=&quot;language-text&quot;&gt;broke the styling completely&lt;/code&gt;. Claude suggested upgrading to Tailwind v4 and the build looked fine, but the actual app was unusable (see &lt;a href=&quot;#32-tailwind-v4-attempt-failure&quot;&gt;section 3&lt;/a&gt;). That kicked off a separate interactive session where we connected Claude Code to Chrome DevTools, opened the real browser, and debugged the issues together. We investigated the CSS output, found the root causes, tried fixes, and ultimately downgraded to Tailwind v3. We also found and fixed the ng-zorro select height bug the same way: look at the page, inspect the element, trace the CSS rules, apply the fix, verify in the browser.&lt;/p&gt;
&lt;p&gt;This is the part that matters. The initial automated run did not have a way to verify its own work visually. It ran &lt;code class=&quot;language-text&quot;&gt;ng build&lt;/code&gt;, got a green checkmark, and moved on. Having a feedback loop where AI can verify the result in a real browser is what makes the difference. When we switched to the interactive session with Chrome DevTools, every fix was verified on screen before moving to the next issue. That loop is what turned a broken app into a working one.&lt;/p&gt;
&lt;h2 id=&quot;8-deploy-output-path-changed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#8-deploy-output-path-changed&quot; aria-label=&quot;8 deploy output path changed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. Deploy output path changed&lt;/h2&gt;
&lt;p&gt;One thing to watch out for: the new &lt;code class=&quot;language-text&quot;&gt;application&lt;/code&gt; builder outputs to &lt;code class=&quot;language-text&quot;&gt;dist/&amp;lt;project-name&gt;/browser&lt;/code&gt; instead of just &lt;code class=&quot;language-text&quot;&gt;dist&lt;/code&gt;. If your deployment platform is configured to serve from &lt;code class=&quot;language-text&quot;&gt;dist&lt;/code&gt;, your site will break silently. No build error, no warning. The deploy just serves an empty directory.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ff30ef19397447a414617d1a38387a25/cd367/11-build-output.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcdA2KMf/8QAGhAAAgIDAAAAAAAAAAAAAAAAAAECEBExQf/aAAgBAQABBQLuIjHuv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAEFAQAAAAAAAAAAAAAAAAABESAhMqH/2gAIAQEABj8CNcKV4f/EABoQAQABBQAAAAAAAAAAAAAAAAEAECExsfD/2gAIAQEAAT8hS6dXCDoTNX//2gAMAwEAAgADAAAAEJAP/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qp//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAMAAQUAAAAAAAAAAAAAAAABESEQMVFhsf/aAAgBAQABPxBLo8cnTCNgm9L0ZyuLoz//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Build output now goes to dist/browser&quot;
        title=&quot;Build output now goes to dist/browser&quot;
        src=&quot;/static/ff30ef19397447a414617d1a38387a25/6a068/11-build-output.jpg&quot;
        srcset=&quot;/static/ff30ef19397447a414617d1a38387a25/09b79/11-build-output.jpg 240w,
/static/ff30ef19397447a414617d1a38387a25/7cc5e/11-build-output.jpg 480w,
/static/ff30ef19397447a414617d1a38387a25/6a068/11-build-output.jpg 960w,
/static/ff30ef19397447a414617d1a38387a25/644c5/11-build-output.jpg 1440w,
/static/ff30ef19397447a414617d1a38387a25/0f98f/11-build-output.jpg 1920w,
/static/ff30ef19397447a414617d1a38387a25/cd367/11-build-output.jpg 2356w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I caught this when deploying to Netlify. The build passed, the deploy succeeded, but the site was blank.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/8ec0a/12-netlify-error.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEFAgT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABkd0p2MyH/8QAGRABAAIDAAAAAAAAAAAAAAAAAwECBBEg/9oACAEBAAEFAjrKI+LYS3PH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAgIDAAAAAAAAAAAAAAAAABECEhMgMf/aAAgBAQAGPwKMGmXyM7p//8QAGhAAAgIDAAAAAAAAAAAAAAAAAREAEDFRYf/aAAgBAQABPyHNypxgApASndTOzX//2gAMAwEAAgADAAAAECff/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAQACAwEAAAAAAAAAAAAAAREAIRAxYXH/2gAIAQEAAT8QJDUD3NYsCQM9+3iKjpT5ixFD1wKZ/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Netlify deploy error&quot;
        title=&quot;Netlify deploy error&quot;
        src=&quot;/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/6a068/12-netlify-error.jpg&quot;
        srcset=&quot;/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/09b79/12-netlify-error.jpg 240w,
/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/7cc5e/12-netlify-error.jpg 480w,
/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/6a068/12-netlify-error.jpg 960w,
/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/644c5/12-netlify-error.jpg 1440w,
/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/0f98f/12-netlify-error.jpg 1920w,
/static/bdbfd1a72ef7fdb8ecc723d45503a2ca/8ec0a/12-netlify-error.jpg 2560w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The fix is to update the publish directory from &lt;code class=&quot;language-text&quot;&gt;dist&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;dist/browser&lt;/code&gt; on Netlify (or whatever hosting you use). It is a one-line config change, but easy to miss because nothing tells you the output path has changed.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/781cc54164de087f067ef48d00ebb424/8b1bd/13-netlify-config.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHjsWyIP//EABYQAQEBAAAAAAAAAAAAAAAAABEAIP/aAAgBAQABBQJnP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABcQAAMBAAAAAAAAAAAAAAAAAAAgQZH/2gAIAQEABj8ChMX/xAAaEAEBAAIDAAAAAAAAAAAAAAABADGBEBEh/9oACAEBAAE/IfODY7lpjQTwzf/aAAwDAQACAAMAAAAQxw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAQUBAAAAAAAAAAAAAAABABEhMUGRcf/aAAgBAQABPxAdivFdSKBOuwPaRXLQzNZlP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Netlify publish directory configuration&quot;
        title=&quot;Netlify publish directory configuration&quot;
        src=&quot;/static/781cc54164de087f067ef48d00ebb424/6a068/13-netlify-config.jpg&quot;
        srcset=&quot;/static/781cc54164de087f067ef48d00ebb424/09b79/13-netlify-config.jpg 240w,
/static/781cc54164de087f067ef48d00ebb424/7cc5e/13-netlify-config.jpg 480w,
/static/781cc54164de087f067ef48d00ebb424/6a068/13-netlify-config.jpg 960w,
/static/781cc54164de087f067ef48d00ebb424/644c5/13-netlify-config.jpg 1440w,
/static/781cc54164de087f067ef48d00ebb424/0f98f/13-netlify-config.jpg 1920w,
/static/781cc54164de087f067ef48d00ebb424/8b1bd/13-netlify-config.jpg 2802w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;9-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#9-source-code&quot; aria-label=&quot;9 source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/114&quot;&gt;https://github.com/trungvose/jira-clone-angular/pull/114&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;10-what-i-learned&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#10-what-i-learned&quot; aria-label=&quot;10 what i learned permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. What I learned&lt;/h2&gt;
&lt;p&gt;Angular 20 itself is a smooth upgrade. The framework changes are minimal if your codebase is already standalone with modern control flow. The real work is everything around it: the build tooling, the test runner, the linter.&lt;/p&gt;
&lt;p&gt;The biggest lesson from this upgrade is not about Angular or Tailwind. It is about verification loops. The initial Claude Code session ran the build, ran the tests, and reported success. But no one opened the browser. The app was completely broken and no automated check caught it.&lt;/p&gt;
&lt;p&gt;When we switched to an interactive session with Claude connected to Chrome DevTools, the workflow changed. Every change was verified on the actual page. We could see what was broken, inspect the CSS, apply a fix, and confirm it worked before moving on. That is the loop that matters. If you are using AI to make changes to your app, give it a way to see the result. &lt;strong&gt;A passing build is not the same as a working app&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On Tailwind specifically: v4 and Angular component styles do not play well together right now. &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; needs &lt;code class=&quot;language-text&quot;&gt;@reference&lt;/code&gt; in every component file, the performance impact is real, and there is no official fix. If your Angular project uses &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; in component styles, stay on v3 until the tooling catches up.&lt;/p&gt;
&lt;p&gt;Looking back at the full series:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Key work&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/&quot;&gt;#1 | 13 to 14&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Manual dependency resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part2-angular-15/&quot;&gt;#2 | 14 to 15&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Standalone components begin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part3-angular-16/&quot;&gt;#3 | 15 to 16&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Signals introduced, more standalone migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part4-angular-17/&quot;&gt;#4 | 16 to 17&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Massive: new control flow syntax, full standalone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part5-angular-18/&quot;&gt;#5 | 17 to 18&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;NgModule removal, Quill v2, route config migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part6-angular-19/&quot;&gt;#6 | 18 to 19&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Simplest: remove standalone flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part7-angular-20/&quot;&gt;#7 | 19 to 20&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Build, test, lint modernization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Seven parts, seven versions, one codebase taken from Angular 13 to Angular 20. The journey from a legacy NgModule-based app with &lt;code class=&quot;language-text&quot;&gt;*ngIf&lt;/code&gt; and Karma to a fully standalone app with &lt;code class=&quot;language-text&quot;&gt;@if&lt;/code&gt;, esbuild, Vitest, and ESLint 9 flat config.&lt;/p&gt;
&lt;p&gt;We are done.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fuzzy search everywhere on macOS with fzf and fd]]></title><link>https://trungvose.comfuzzy-search-everywhere-macos-fzf-fd/</link><guid isPermaLink="false">https://trungvose.comfuzzy-search-everywhere-macos-fzf-fd/</guid><pubDate>Fri, 27 Mar 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I spend a lot of time jumping between project folders. Open a terminal, type &lt;code class=&quot;language-text&quot;&gt;cd&lt;/code&gt;, tab-complete through nested directories, realise I went the wrong way, backtrack. Repeat.&lt;/p&gt;
&lt;p&gt;I wanted something faster. A global shortcut that opens a fuzzy finder, I type a few characters, hit enter, and I am in the right folder with my editor open.&lt;/p&gt;
&lt;p&gt;That inspiration came from &lt;a href=&quot;https://www.youtube.com/@ThePrimeTimeagen&quot;&gt;ThePrimeagen&lt;/a&gt;. I watched one of his courses on &lt;a href=&quot;https://frontendmasters.com/courses/developer-productivity-v2/&quot;&gt;Frontend Masters&lt;/a&gt; last year about setting up the development environment. He mentioned having a global shortcut to open a fuzzy finder to jump to any folder. The tricky part? He did not exactly explain how he configured the workflow.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dfb39c42db72a94f85a05394a83069c8/a3eaf/01-primeagen.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHjSo4xgf/EABoQAAICAwAAAAAAAAAAAAAAAAABAhESITH/2gAIAQEAAQUClxXeLLIvaP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAABAgMf/aAAgBAQAGPwIRP//EABsQAQACAgMAAAAAAAAAAAAAAAEAERAxIUFR/9oACAEBAAE/IQ6EFPDvCt6xOzE1tn//2gAMAwEAAgADAAAAENMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAAIDAQAAAAAAAAAAAAAAAQARITFRwf/aAAgBAQABPxBdUWOEsREnKjT7RuWrser2dn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ThePrimeagen course on Frontend Masters&quot;
        title=&quot;ThePrimeagen course on Frontend Masters&quot;
        src=&quot;/static/dfb39c42db72a94f85a05394a83069c8/6a068/01-primeagen.jpg&quot;
        srcset=&quot;/static/dfb39c42db72a94f85a05394a83069c8/09b79/01-primeagen.jpg 240w,
/static/dfb39c42db72a94f85a05394a83069c8/7cc5e/01-primeagen.jpg 480w,
/static/dfb39c42db72a94f85a05394a83069c8/6a068/01-primeagen.jpg 960w,
/static/dfb39c42db72a94f85a05394a83069c8/644c5/01-primeagen.jpg 1440w,
/static/dfb39c42db72a94f85a05394a83069c8/0f98f/01-primeagen.jpg 1920w,
/static/dfb39c42db72a94f85a05394a83069c8/a3eaf/01-primeagen.jpg 2690w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So I went looking, and found &lt;code class=&quot;language-text&quot;&gt;fzf&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-is-fzf&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-fzf&quot; aria-label=&quot;what is fzf permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is fzf&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;fzf&lt;/code&gt; is a command-line fuzzy finder. You pipe a list of things into it, it lets you type to filter, and it returns what you pick. That is the whole idea.&lt;/p&gt;
&lt;p&gt;Install it with Homebrew:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; fzf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After installing, just type &lt;code class=&quot;language-text&quot;&gt;fzf&lt;/code&gt; in your terminal. It will list every file on your machine and let you search through them.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;fzf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/9c45c3fd3b90875d37b9d59171eff424/02-fzf-basic.gif&quot; alt=&quot;fzf basic usage in terminal&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;You type a few characters, the list narrows down, you press Enter, and it prints the selected path. Simple.&lt;/p&gt;
&lt;h2 id=&quot;fzf-preview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fzf-preview&quot; aria-label=&quot;fzf preview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;fzf preview&lt;/h2&gt;
&lt;p&gt;One useful feature is the preview window. You can tell fzf to show the content of whatever file you have highlighted:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;fzf &lt;span class=&quot;token parameter variable&quot;&gt;--preview&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cat {}&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This opens a split view. The left side is your search list, the right side shows the file content. Useful when you are looking for a specific file but do not remember the exact name.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/92ad03c5c0e17e11cd8d413033a3f937/03-fzf-preview.gif&quot; alt=&quot;fzf preview showing file content&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can customise the preview command too. For example, if you have &lt;code class=&quot;language-text&quot;&gt;bat&lt;/code&gt; installed (a &lt;code class=&quot;language-text&quot;&gt;cat&lt;/code&gt; alternative with syntax highlighting):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;fzf &lt;span class=&quot;token parameter variable&quot;&gt;--preview&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bat --color=always {}&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-i-actually-needed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-i-actually-needed&quot; aria-label=&quot;what i actually needed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What I actually needed&lt;/h2&gt;
&lt;p&gt;The default fzf experience is nice for searching files. But what I wanted was different. I wanted to search for &lt;strong&gt;directories&lt;/strong&gt;, not files. And when I pick one, I want it to open in my editor. Not print a path. Not cd into it. Just open the project.&lt;/p&gt;
&lt;p&gt;So the question became: how do I list only directories, scope the search to folders I care about, and wire it up to a shortcut?&lt;/p&gt;
&lt;h2 id=&quot;why-fd-over-find&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-fd-over-find&quot; aria-label=&quot;why fd over find permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why fd over find&lt;/h2&gt;
&lt;p&gt;By default, fzf uses &lt;code class=&quot;language-text&quot;&gt;find&lt;/code&gt; to list files. It works, but &lt;code class=&quot;language-text&quot;&gt;find&lt;/code&gt; is slow on large directory trees and has no concept of ignore rules. It will happily crawl through &lt;code class=&quot;language-text&quot;&gt;node_modules/&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.git/&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Library/&lt;/code&gt;, and every other folder you never want to see.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt; is a faster alternative to &lt;code class=&quot;language-text&quot;&gt;find&lt;/code&gt;. Two things make it worth switching:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It is significantly faster, especially on large directory trees&lt;/li&gt;
&lt;li&gt;It respects &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;.ignore&lt;/code&gt; files by default. See &lt;a href=&quot;https://github.com/sharkdp/fd&quot;&gt;reference&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/60e10ec5558be95d8b1a9e106c4612a1/a7229/04-fd-ignore.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEDAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHkKkzAw//EABcQAQADAAAAAAAAAAAAAAAAAAABICH/2gAIAQEAAQUCZSX/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEQIUFRYf/aAAgBAQABPyFo4JfiNXrCvcW5/9oADAMBAAIAAwAAABArL//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB4QAQACAgEFAAAAAAAAAAAAAAEAESFBcTFRkeHx/9oACAEBAAE/EMK+ht9RHT4+RpUog4OJacvMRFq47z//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;fd respects .gitignore by default&quot;
        title=&quot;fd respects .gitignore by default&quot;
        src=&quot;/static/60e10ec5558be95d8b1a9e106c4612a1/6a068/04-fd-ignore.jpg&quot;
        srcset=&quot;/static/60e10ec5558be95d8b1a9e106c4612a1/09b79/04-fd-ignore.jpg 240w,
/static/60e10ec5558be95d8b1a9e106c4612a1/7cc5e/04-fd-ignore.jpg 480w,
/static/60e10ec5558be95d8b1a9e106c4612a1/6a068/04-fd-ignore.jpg 960w,
/static/60e10ec5558be95d8b1a9e106c4612a1/644c5/04-fd-ignore.jpg 1440w,
/static/60e10ec5558be95d8b1a9e106c4612a1/a7229/04-fd-ignore.jpg 1638w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That second point is the real reason. You can define once which folders to include and exclude, and &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt; will follow those rules every time.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; fd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On Debian/Ubuntu the binary is called &lt;code class=&quot;language-text&quot;&gt;fdfind&lt;/code&gt;, but on macOS via Homebrew it is just &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt;. If you see tutorials using &lt;code class=&quot;language-text&quot;&gt;fdfind&lt;/code&gt;, swap it out.&lt;/p&gt;
&lt;p&gt;If you just run &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt; by itself, it lists all files and folders it can find. To list only directories, use the &lt;code class=&quot;language-text&quot;&gt;--type d&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;fd &lt;span class=&quot;token parameter variable&quot;&gt;--type&lt;/span&gt; d&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On its own, that still dumps the full list to the terminal. That is where we pipe it through &lt;code class=&quot;language-text&quot;&gt;fzf&lt;/code&gt; to get fuzzy search over the results:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;fd &lt;span class=&quot;token parameter variable&quot;&gt;--type&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; fzf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/fb33bddc3189658f29e55099f525861c/05-fd-directory-fzf.gif&quot; alt=&quot;Fuzzy searching directories with fd and fzf&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now you get a searchable list of only directories. Type a few characters, the list narrows, pick one. That is the core of the whole setup.&lt;/p&gt;
&lt;h2 id=&quot;control-what-fd-searches-with-ignore&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#control-what-fd-searches-with-ignore&quot; aria-label=&quot;control what fd searches with ignore permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Control what fd searches with .ignore&lt;/h2&gt;
&lt;p&gt;By default, &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt; will crawl everything under your home directory. You do not want that. Thousands of folders inside &lt;code class=&quot;language-text&quot;&gt;Library/&lt;/code&gt; will flood your results.&lt;/p&gt;
&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;~/.ignore&lt;/code&gt; file. The trick is to block everything first, then allow list the folders you actually care about using &lt;code class=&quot;language-text&quot;&gt;!&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;To get the list of folder from root, run the following command&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~/
&lt;span class=&quot;token function&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; */ &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; pbcopy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;Applications/
Desktop/
Documents/
Library/
Movies/
Music/
Pictures/
Public/
bin/
go/
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; !Downloads/
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; !Source/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lines without &lt;code class=&quot;language-text&quot;&gt;!&lt;/code&gt; are ignored by &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt;. Lines with &lt;code class=&quot;language-text&quot;&gt;!&lt;/code&gt; are re-included. So in this setup, only &lt;code class=&quot;language-text&quot;&gt;Downloads/&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Source/&lt;/code&gt; get searched. Clean results, fast searches.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fc9dcf7529ce7d377a7c1c708c60aa61/2bfae/06-ignore-file.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIDBAX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHPVFLZGH//xAAbEAABBAMAAAAAAAAAAAAAAAACAAEDEBETMf/aAAgBAQABBQJ5DWw0Mh4eh5//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAACAwEAAAAAAAAAAAAAAAAAMgECkSD/2gAIAQEABj8CadHto08f/8QAGhAAAgMBAQAAAAAAAAAAAAAAAAERITGR8f/aAAgBAQABPyGfmPcDVu5uhWLGj//aAAwDAQACAAMAAAAQEM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAEBAQACAgMAAAAAAAAAAAABEQAhUTFhcZHR/9oACAEBAAE/EBMPjIwbfu/bNlDZ56OaW94VLMKFPPXrf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The .ignore file content&quot;
        title=&quot;The .ignore file content&quot;
        src=&quot;/static/fc9dcf7529ce7d377a7c1c708c60aa61/6a068/06-ignore-file.jpg&quot;
        srcset=&quot;/static/fc9dcf7529ce7d377a7c1c708c60aa61/09b79/06-ignore-file.jpg 240w,
/static/fc9dcf7529ce7d377a7c1c708c60aa61/7cc5e/06-ignore-file.jpg 480w,
/static/fc9dcf7529ce7d377a7c1c708c60aa61/6a068/06-ignore-file.jpg 960w,
/static/fc9dcf7529ce7d377a7c1c708c60aa61/644c5/06-ignore-file.jpg 1440w,
/static/fc9dcf7529ce7d377a7c1c708c60aa61/2bfae/06-ignore-file.jpg 1654w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;wire-it-up-in-zshrc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wire-it-up-in-zshrc&quot; aria-label=&quot;wire it up in zshrc permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Wire it up in .zshrc&lt;/h2&gt;
&lt;p&gt;Tell fzf to use &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt; as its default search command, and add a helper function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;FZF_DEFAULT_COMMAND&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fd . &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;FZF_DEFAULT_OPTS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--bind &apos;alt-down:first,alt-up:last&apos;&quot;&lt;/span&gt;

&lt;span class=&quot;token function-name function&quot;&gt;fzf-open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;fd &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--type&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; fzf&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    agy &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you type &lt;code class=&quot;language-text&quot;&gt;fzf-open&lt;/code&gt;, it lists all directories under &lt;code class=&quot;language-text&quot;&gt;$HOME&lt;/code&gt; (filtered by &lt;code class=&quot;language-text&quot;&gt;.ignore&lt;/code&gt;), pipes them into fzf, and opens the selected folder. Replace &lt;code class=&quot;language-text&quot;&gt;agy&lt;/code&gt; with whatever you use to open projects, could be &lt;code class=&quot;language-text&quot;&gt;code&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;cursor&lt;/code&gt;, or just &lt;code class=&quot;language-text&quot;&gt;cd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;FZF_DEFAULT_OPTS&lt;/code&gt; line adds Alt+Down to jump to the first result and Alt+Up to jump to the last. On Mac, the default Cmd+Up/Down controls text navigation, so fzf cannot use the Command key directly. There is a section below on how to remap that. Useful when you have scrolled through a long list and want to get back quickly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/611d1594f908e8640f38c19b5f4b012a/07-fzf-agy.gif&quot; alt=&quot;fzf-open function selecting and opening a project&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;iterm2-hotkey-to-trigger-fzf-open&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#iterm2-hotkey-to-trigger-fzf-open&quot; aria-label=&quot;iterm2 hotkey to trigger fzf open permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;iTerm2 hotkey to trigger fzf-open&lt;/h2&gt;
&lt;p&gt;This is the part that makes it feel seamless. Instead of opening a terminal and typing &lt;code class=&quot;language-text&quot;&gt;fzf-open&lt;/code&gt;, I wanted a single keyboard shortcut from anywhere on my Mac to open a terminal and run fzf-open automatically.&lt;/p&gt;
&lt;p&gt;iTerm2 has a Hotkey Window feature that does exactly this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open iTerm2 Settings, go to &lt;strong&gt;Profiles&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Create a new profile called &lt;code class=&quot;language-text&quot;&gt;fzf-open&lt;/code&gt;, set the command to &lt;code class=&quot;language-text&quot;&gt;zsh -lic &apos;fzf-open; exec zsh&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Keys&lt;/strong&gt;, check &lt;strong&gt;A hotkey opens a dedicated window with this profile&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Set your preferred shortcut&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/39dc0a230775a342fd2f880565601f84/f6225/08-iterm-profile.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7NSGD/xAAXEAEBAQEAAAAAAAAAAAAAAAABABAh/9oACAEBAAEFAuxLrf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAMBAAMAAAAAAAAAAAAAAAABESExQWH/2gAIAQEAAT8hnaHpkHhRcmmf/9oADAMBAAIAAwAAABAQD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQADAQEBAAAAAAAAAAAAAAEAESFBUTH/2gAIAQEAAT8Q0vE+VdxPqHYhAErpFqGUkFJmRfJ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Creating the fzf-open profile in iTerm2&quot;
        title=&quot;Creating the fzf-open profile in iTerm2&quot;
        src=&quot;/static/39dc0a230775a342fd2f880565601f84/6a068/08-iterm-profile.jpg&quot;
        srcset=&quot;/static/39dc0a230775a342fd2f880565601f84/09b79/08-iterm-profile.jpg 240w,
/static/39dc0a230775a342fd2f880565601f84/7cc5e/08-iterm-profile.jpg 480w,
/static/39dc0a230775a342fd2f880565601f84/6a068/08-iterm-profile.jpg 960w,
/static/39dc0a230775a342fd2f880565601f84/644c5/08-iterm-profile.jpg 1440w,
/static/39dc0a230775a342fd2f880565601f84/0f98f/08-iterm-profile.jpg 1920w,
/static/39dc0a230775a342fd2f880565601f84/f6225/08-iterm-profile.jpg 2124w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a1f7baeb3faf5b7591f61f57b17a9f7c/c9af3/09-iterm-hotkey.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMCBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7OkzYP/8QAFhAAAwAAAAAAAAAAAAAAAAAAACBB/9oACAEBAAEFAkp//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGxABAQABBQAAAAAAAAAAAAAAAQAxEBEhQaH/2gAIAQEAAT8h57fJXfMLowxf/9oADAMBAAIAAwAAABDTz//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/EIf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAwEAAwAAAAAAAAAAAAABABEhMUFRcf/aAAgBAQABPxBDoJ44iqi+ShuS1afUJTDsJTJ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring the hotkey shortcut&quot;
        title=&quot;Configuring the hotkey shortcut&quot;
        src=&quot;/static/a1f7baeb3faf5b7591f61f57b17a9f7c/6a068/09-iterm-hotkey.jpg&quot;
        srcset=&quot;/static/a1f7baeb3faf5b7591f61f57b17a9f7c/09b79/09-iterm-hotkey.jpg 240w,
/static/a1f7baeb3faf5b7591f61f57b17a9f7c/7cc5e/09-iterm-hotkey.jpg 480w,
/static/a1f7baeb3faf5b7591f61f57b17a9f7c/6a068/09-iterm-hotkey.jpg 960w,
/static/a1f7baeb3faf5b7591f61f57b17a9f7c/644c5/09-iterm-hotkey.jpg 1440w,
/static/a1f7baeb3faf5b7591f61f57b17a9f7c/0f98f/09-iterm-hotkey.jpg 1920w,
/static/a1f7baeb3faf5b7591f61f57b17a9f7c/c9af3/09-iterm-hotkey.jpg 2436w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;make-it-fast-with-a-dedicated-script&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#make-it-fast-with-a-dedicated-script&quot; aria-label=&quot;make it fast with a dedicated script permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Make it fast with a dedicated script&lt;/h3&gt;
&lt;p&gt;My first attempt was to set the profile command to &lt;code class=&quot;language-text&quot;&gt;zsh -lic &apos;fzf-open; exec zsh&apos;&lt;/code&gt;. It worked, but it was slow. Every time I triggered the shortcut, zsh would load my entire &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; - oh-my-zsh, powerlevel10k, nvm, asdf, pyenv, plugins, completions. All that just to run &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;fzf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The fix is to skip &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; entirely. Create a minimal script at &lt;code class=&quot;language-text&quot;&gt;~/.fzf-open.sh&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/zsh&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/opt/homebrew/bin:&lt;span class=&quot;token environment constant&quot;&gt;$PATH&lt;/span&gt;:&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.antigravity/antigravity/bin&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;fd &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--type&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; fzf &lt;span class=&quot;token parameter variable&quot;&gt;--bind&lt;/span&gt; alt-down:first,alt-up:last&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; agy &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/80a5e539226860ac08bac1f8fbbf3bac/2bfae/10-fzf-open-cat.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeewFacf/8QAGRAAAwEBAQAAAAAAAAAAAAAAAAECAxAT/9oACAEBAAEFAnrYtbPW+o//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAADAQEAAAAAAAAAAAAAAAAAAjKRIP/aAAgBAQAGPwKm0ptLbj//xAAYEAEBAAMAAAAAAAAAAAAAAAABABAx0f/aAAgBAQABPyFTQXZD5Nb/2gAMAwEAAgADAAAAEIDP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHRABAAEDBQAAAAAAAAAAAAAAAQARMbEQIUFxgf/aAAgBAQABPxBAlRKXGGJ2vbLDVW35db/c/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The fzf-open.sh script content&quot;
        title=&quot;The fzf-open.sh script content&quot;
        src=&quot;/static/80a5e539226860ac08bac1f8fbbf3bac/6a068/10-fzf-open-cat.jpg&quot;
        srcset=&quot;/static/80a5e539226860ac08bac1f8fbbf3bac/09b79/10-fzf-open-cat.jpg 240w,
/static/80a5e539226860ac08bac1f8fbbf3bac/7cc5e/10-fzf-open-cat.jpg 480w,
/static/80a5e539226860ac08bac1f8fbbf3bac/6a068/10-fzf-open-cat.jpg 960w,
/static/80a5e539226860ac08bac1f8fbbf3bac/644c5/10-fzf-open-cat.jpg 1440w,
/static/80a5e539226860ac08bac1f8fbbf3bac/2bfae/10-fzf-open-cat.jpg 1654w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Make it executable:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x ~/.fzf-open.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then set the iTerm2 profile command to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;zsh&lt;/span&gt; ~/.fzf-open.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8087a1a2689c484f183ba9173bcf9501/f6225/11-iterm-fzf-open.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7NSYD/xAAXEAEBAQEAAAAAAAAAAAAAAAABABAh/9oACAEBAAEFAuxLrf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAAMAAgMAAAAAAAAAAAAAAAABESExQVFh/9oACAEBAAE/IfUg3bIPELwLZkz/2gAMAwEAAgADAAAAEBMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAQEAAwEAAAAAAAAAAAAAAREAIUGBUf/aAAgBAQABPxAA3xXJYLz7oxQneWtSJgCcYL83/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;iTerm2 profile command pointing to fzf-open.sh&quot;
        title=&quot;iTerm2 profile command pointing to fzf-open.sh&quot;
        src=&quot;/static/8087a1a2689c484f183ba9173bcf9501/6a068/11-iterm-fzf-open.jpg&quot;
        srcset=&quot;/static/8087a1a2689c484f183ba9173bcf9501/09b79/11-iterm-fzf-open.jpg 240w,
/static/8087a1a2689c484f183ba9173bcf9501/7cc5e/11-iterm-fzf-open.jpg 480w,
/static/8087a1a2689c484f183ba9173bcf9501/6a068/11-iterm-fzf-open.jpg 960w,
/static/8087a1a2689c484f183ba9173bcf9501/644c5/11-iterm-fzf-open.jpg 1440w,
/static/8087a1a2689c484f183ba9173bcf9501/0f98f/11-iterm-fzf-open.jpg 1920w,
/static/8087a1a2689c484f183ba9173bcf9501/f6225/11-iterm-fzf-open.jpg 2124w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This script only sets the PATH needed for &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;fzf&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;agy&lt;/code&gt;. Nothing else loads. The difference is noticeable - fzf appears almost instantly instead of waiting a second or two for the full shell to initialise.&lt;/p&gt;
&lt;h2 id=&quot;cmdup-and-cmddown-in-fzf&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cmdup-and-cmddown-in-fzf&quot; aria-label=&quot;cmdup and cmddown in fzf permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cmd+Up and Cmd+Down in fzf&lt;/h2&gt;
&lt;p&gt;fzf does not support the Command key natively. The only modifiers it recognises are &lt;code class=&quot;language-text&quot;&gt;ctrl-&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;alt-&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;shift-&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But iTerm2 can remap keys. So I mapped Cmd to send Alt escape sequences:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to iTerm2 Settings, &lt;strong&gt;Profiles&lt;/strong&gt;, &lt;strong&gt;Keys&lt;/strong&gt;, &lt;strong&gt;Key Mappings&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;strong&gt;Cmd+Down&lt;/strong&gt; with action &lt;strong&gt;Send Hex Code&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;0x1b 0x5b 0x31 0x3b 0x33 0x42&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;strong&gt;Cmd+Up&lt;/strong&gt; with action &lt;strong&gt;Send Hex Code&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;0x1b 0x5b 0x31 0x3b 0x33 0x41&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This part was largely suggested by Claude. I have not checked why it maps to these specific hex codes, but they are the escape sequences for Alt+Down and Alt+Up. iTerm2 intercepts the Cmd keypress and sends what fzf actually understands.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/615842ce10cf5e81e017dce961ead599/f319e/12-iterm-cmd-key.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABySyGo//EABYQAAMAAAAAAAAAAAAAAAAAAAEQIP/aAAgBAQABBQKCv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAIDAAAAAAAAAAAAAAAAABARAAEh/9oACAEBAAE/IVLDwv/aAAwDAQACAAMAAAAQAw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAACAwEBAAAAAAAAAAAAAAAAAREhUWFx/9oACAEBAAE/EHRPfClOSuiwRRIl6f/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;iTerm2 key mapping for Cmd to Alt&quot;
        title=&quot;iTerm2 key mapping for Cmd to Alt&quot;
        src=&quot;/static/615842ce10cf5e81e017dce961ead599/6a068/12-iterm-cmd-key.jpg&quot;
        srcset=&quot;/static/615842ce10cf5e81e017dce961ead599/09b79/12-iterm-cmd-key.jpg 240w,
/static/615842ce10cf5e81e017dce961ead599/7cc5e/12-iterm-cmd-key.jpg 480w,
/static/615842ce10cf5e81e017dce961ead599/6a068/12-iterm-cmd-key.jpg 960w,
/static/615842ce10cf5e81e017dce961ead599/644c5/12-iterm-cmd-key.jpg 1440w,
/static/615842ce10cf5e81e017dce961ead599/0f98f/12-iterm-cmd-key.jpg 1920w,
/static/615842ce10cf5e81e017dce961ead599/f319e/12-iterm-cmd-key.jpg 2496w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now Cmd+Down jumps to the first item and Cmd+Up jumps to the last.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/8757e03408b2629a8b6cc42c3ca80461/13-cmd-top-last.gif&quot; alt=&quot;Cmd+Up and Cmd+Down navigating fzf results&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;final-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-result&quot; aria-label=&quot;final result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final Result&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/df69161d2e52c5f47a90839c761be9fd/14-final-result.gif&quot; alt=&quot;Fuzzy searching and opening a project from anywhere on macOS&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;One keyboard shortcut from anywhere on macOS. A fuzzy search scoped to only the folders I care about. Pick a folder, hit enter, project opens. The whole thing takes maybe two seconds.&lt;/p&gt;
&lt;p&gt;The setup is five pieces working together: &lt;code class=&quot;language-text&quot;&gt;fd&lt;/code&gt; for fast directory listing, &lt;code class=&quot;language-text&quot;&gt;.ignore&lt;/code&gt; for scoping, &lt;code class=&quot;language-text&quot;&gt;fzf&lt;/code&gt; for fuzzy matching, a shell function to glue it together, and iTerm2 hotkey window to make it globally accessible.&lt;/p&gt;
&lt;p&gt;Such a good way to bring joy to my daily work.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Speed up zsh startup by lazy loading nvm]]></title><link>https://trungvose.comspeed-up-zsh-startup-lazy-loading-nvm/</link><guid isPermaLink="false">https://trungvose.comspeed-up-zsh-startup-lazy-loading-nvm/</guid><pubDate>Tue, 24 Mar 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every time I opened a new terminal, there was a noticeable delay. About one second of staring at a blank screen before the prompt appeared. Not terrible, but it added up. Open a terminal to run a quick command, wait. Split a new pane for Claude Code, wait. After a while, I decided to figure out what was causing it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/9fc58fb637af62a7d69e0e93c80a439a/01-zsh-start.gif&quot; alt=&quot;Slow zsh startup&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Since I use Claude Code for almost everything now, I started a session and asked it to investigate. It profiled my shell, found the bottleneck, and suggested the fix. Here is what we found.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/77450b1a8de2f812373ac279702599f7/ae398/00-claude.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.666666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByiiGo//EABUQAQEAAAAAAAAAAAAAAAAAABAB/9oACAEBAAEFAmP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAbEAACAQUAAAAAAAAAAAAAAAAAAREQITFhgf/aAAgBAQABPyGZWBvSOCWIp//aAAwDAQACAAMAAAAQAw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAgMBAAAAAAAAAAAAAAABACEQEUEx/9oACAEBAAE/EPchroyyprviAnZyIXH/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude prompt&quot;
        title=&quot;Claude prompt&quot;
        src=&quot;/static/77450b1a8de2f812373ac279702599f7/6a068/00-claude.jpg&quot;
        srcset=&quot;/static/77450b1a8de2f812373ac279702599f7/09b79/00-claude.jpg 240w,
/static/77450b1a8de2f812373ac279702599f7/7cc5e/00-claude.jpg 480w,
/static/77450b1a8de2f812373ac279702599f7/6a068/00-claude.jpg 960w,
/static/77450b1a8de2f812373ac279702599f7/644c5/00-claude.jpg 1440w,
/static/77450b1a8de2f812373ac279702599f7/0f98f/00-claude.jpg 1920w,
/static/77450b1a8de2f812373ac279702599f7/ae398/00-claude.jpg 2046w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;profiling-with-zprof&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#profiling-with-zprof&quot; aria-label=&quot;profiling with zprof permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Profiling with zprof&lt;/h2&gt;
&lt;p&gt;zsh has a built-in profiler called &lt;code class=&quot;language-text&quot;&gt;zprof&lt;/code&gt;. It is part of the &lt;code class=&quot;language-text&quot;&gt;zsh/zprof&lt;/code&gt; module, and it tells you exactly how long each function takes during shell initialisation.&lt;/p&gt;
&lt;p&gt;To use it, add this line to the &lt;strong&gt;top&lt;/strong&gt; of your &lt;code class=&quot;language-text&quot;&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;zmodload zsh/zprof&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And this line to the &lt;strong&gt;bottom&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;zprof&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then restart your shell:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;zsh&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When zsh starts, &lt;code class=&quot;language-text&quot;&gt;zprof&lt;/code&gt; loads first and starts recording. After everything else in &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; finishes, &lt;code class=&quot;language-text&quot;&gt;zprof&lt;/code&gt; prints a table showing every function that ran and how long it took.&lt;/p&gt;
&lt;p&gt;You can also run it as a one-liner without editing your &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;zsh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;zmodload zsh/zprof; source ~/.zshrc 2&gt;/dev/null; zprof&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;nvm-is-the-culprit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nvm-is-the-culprit&quot; aria-label=&quot;nvm is the culprit permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;nvm is the culprit&lt;/h2&gt;
&lt;p&gt;Here is what my profiling output looked like:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b1a3833105680ead5b766c96cb51ca1c/89a8f/02-zsh-before.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeTNUZA//8QAFRABAQAAAAAAAAAAAAAAAAAAEAH/2gAIAQEAAQUCY//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAAAABEBExUf/aAAgBAQABPyFYU+FQWj0//9oADAMBAAIAAwAAABBTz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAgMAAAAAAAAAAAAAAAEAETFRECFx/9oACAEBAAE/EAudtbi0W1Fnw4uin//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zprof output before lazy loading nvm&quot;
        title=&quot;zprof output before lazy loading nvm&quot;
        src=&quot;/static/b1a3833105680ead5b766c96cb51ca1c/6a068/02-zsh-before.jpg&quot;
        srcset=&quot;/static/b1a3833105680ead5b766c96cb51ca1c/09b79/02-zsh-before.jpg 240w,
/static/b1a3833105680ead5b766c96cb51ca1c/7cc5e/02-zsh-before.jpg 480w,
/static/b1a3833105680ead5b766c96cb51ca1c/6a068/02-zsh-before.jpg 960w,
/static/b1a3833105680ead5b766c96cb51ca1c/644c5/02-zsh-before.jpg 1440w,
/static/b1a3833105680ead5b766c96cb51ca1c/0f98f/02-zsh-before.jpg 1920w,
/static/b1a3833105680ead5b766c96cb51ca1c/89a8f/02-zsh-before.jpg 2216w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And here is the output of &lt;code class=&quot;language-text&quot;&gt;time zsh -i -c exit&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;zsh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; /dev/null  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.39s user &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.60s system &lt;span class=&quot;token number&quot;&gt;88&lt;/span&gt;% cpu &lt;span class=&quot;token number&quot;&gt;1.122&lt;/span&gt; total&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let me break down the top rows:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;% of startup&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;nvm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;486ms&lt;/td&gt;
&lt;td&gt;49.5%&lt;/td&gt;
&lt;td&gt;The core nvm function that sets up the correct Node.js version and PATH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;nvm_ensure_version_installed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;188ms&lt;/td&gt;
&lt;td&gt;19.1%&lt;/td&gt;
&lt;td&gt;Checks that the Node version nvm wants to use is actually installed on disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;compinit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;316ms&lt;/td&gt;
&lt;td&gt;32.2%&lt;/td&gt;
&lt;td&gt;Initialises zsh’s tab completion system, scanning and compiling all completion functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;compdef&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;111ms&lt;/td&gt;
&lt;td&gt;11.3%&lt;/td&gt;
&lt;td&gt;Registers completion functions for specific commands, called 811 times by compinit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;nvm_auto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;597ms&lt;/td&gt;
&lt;td&gt;60.8%&lt;/td&gt;
&lt;td&gt;The entry point that triggers nvm on shell startup, calls &lt;code class=&quot;language-text&quot;&gt;nvm&lt;/code&gt; internally&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;strong&gt;time&lt;/strong&gt; column includes time spent in sub-functions. So &lt;code class=&quot;language-text&quot;&gt;nvm_auto&lt;/code&gt; (597ms) calls &lt;code class=&quot;language-text&quot;&gt;nvm&lt;/code&gt; (486ms) which calls &lt;code class=&quot;language-text&quot;&gt;nvm_ensure_version_installed&lt;/code&gt; (188ms). They overlap, you cannot add them together. The real total for nvm is &lt;code class=&quot;language-text&quot;&gt;nvm_auto&lt;/code&gt;’s 597ms.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;compinit&lt;/code&gt; row looks high here (316ms) because I had just cleared the zsh completion cache before this run. Normally it loads from a cached &lt;code class=&quot;language-text&quot;&gt;.zcompdump&lt;/code&gt; file and takes around 30ms. So it is not a real concern.&lt;/p&gt;
&lt;p&gt;The actual bottleneck is nvm. It accounts for &lt;strong&gt;60%&lt;/strong&gt; of the total startup time. Every time I opened a terminal, nvm was checking which Node version to use, verifying it was installed, and setting up the environment. All before I even typed anything.&lt;/p&gt;
&lt;p&gt;The rest, oh-my-zsh, powerlevel10k, syntax highlighting, were all fast enough that I would never notice them.&lt;/p&gt;
&lt;h2 id=&quot;lazy-loading-nvm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lazy-loading-nvm&quot; aria-label=&quot;lazy loading nvm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lazy loading nvm&lt;/h2&gt;
&lt;p&gt;The fix is simple. Instead of loading nvm immediately when the shell starts, load it the first time you actually use it. If you open a terminal just to run &lt;code class=&quot;language-text&quot;&gt;git status&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;docker compose up&lt;/code&gt;, there is no reason to wait for nvm.&lt;/p&gt;
&lt;p&gt;Replace this in your &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;NVM_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.nvm&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;. &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/bash_completion&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;. &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/bash_completion&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;NVM_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.nvm&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Lazy load nvm - only initialise when first used&lt;/span&gt;
&lt;span class=&quot;token function-name function&quot;&gt;nvm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  unfunction nvm &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; npx
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;. &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt;
  nvm &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function-name function&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  unfunction nvm &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; npx
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;. &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function-name function&quot;&gt;npm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  unfunction nvm &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; npx
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;. &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function-name function&quot;&gt;npx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  unfunction nvm &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; npx
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;. &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$NVM_DIR&lt;/span&gt;/nvm.sh&quot;&lt;/span&gt;
  npx &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each function is a placeholder. The first time you run &lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;npx&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;nvm&lt;/code&gt;, it removes all the placeholder functions, loads the real nvm, and then runs your command. From that point on, nvm is fully loaded and everything works normally.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;unfunction&lt;/code&gt; call removes all four placeholders at once. This way, after any one of them triggers the load, the others do not accidentally try to load nvm a second time.&lt;/p&gt;
&lt;h2 id=&quot;ater-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ater-result&quot; aria-label=&quot;ater result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ater result&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a825df8e1fdd46419cb713f805fccc03/24688/03-zsh-after.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcqAoD//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAXEAEAAwAAAAAAAAAAAAAAAAABABAx/9oACAEBAAE/ITI1/9oADAMBAAIAAwAAABBzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAEEAwEAAAAAAAAAAAAAAAEAESExQVFhkf/aAAgBAQABPxCdCULis7lF++r/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zprof output after lazy loading nvm&quot;
        title=&quot;zprof output after lazy loading nvm&quot;
        src=&quot;/static/a825df8e1fdd46419cb713f805fccc03/6a068/03-zsh-after.jpg&quot;
        srcset=&quot;/static/a825df8e1fdd46419cb713f805fccc03/09b79/03-zsh-after.jpg 240w,
/static/a825df8e1fdd46419cb713f805fccc03/7cc5e/03-zsh-after.jpg 480w,
/static/a825df8e1fdd46419cb713f805fccc03/6a068/03-zsh-after.jpg 960w,
/static/a825df8e1fdd46419cb713f805fccc03/644c5/03-zsh-after.jpg 1440w,
/static/a825df8e1fdd46419cb713f805fccc03/0f98f/03-zsh-after.jpg 1920w,
/static/a825df8e1fdd46419cb713f805fccc03/24688/03-zsh-after.jpg 2618w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After lazy loading (cold start, no completion cache):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;% of startup&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;compinit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;331ms&lt;/td&gt;
&lt;td&gt;79.8%&lt;/td&gt;
&lt;td&gt;Rebuilds zsh tab completion cache from scratch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;compdef&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;109ms&lt;/td&gt;
&lt;td&gt;26.3%&lt;/td&gt;
&lt;td&gt;Registers completion functions, called 810 times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;compdump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;79ms&lt;/td&gt;
&lt;td&gt;19.0%&lt;/td&gt;
&lt;td&gt;Writes the completion cache to disk for next time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;zsh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.19s user &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.25s system &lt;span class=&quot;token number&quot;&gt;85&lt;/span&gt;% cpu &lt;span class=&quot;token number&quot;&gt;0.508&lt;/span&gt; total&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After lazy loading (warm start, completion cache exists):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;% of startup&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;_omz_source&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;52ms&lt;/td&gt;
&lt;td&gt;59.9%&lt;/td&gt;
&lt;td&gt;Loads oh-my-zsh plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;compaudit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;11ms&lt;/td&gt;
&lt;td&gt;13.1%&lt;/td&gt;
&lt;td&gt;Checks completion directory permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;compinit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;19ms&lt;/td&gt;
&lt;td&gt;22.0%&lt;/td&gt;
&lt;td&gt;Loads zsh tab completion from cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;zsh&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.07s user &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;.08s system &lt;span class=&quot;token number&quot;&gt;84&lt;/span&gt;% cpu &lt;span class=&quot;token number&quot;&gt;0.171&lt;/span&gt; total&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;nvm is completely gone from both. On a warm start, that is &lt;strong&gt;1.12 seconds&lt;/strong&gt; down to &lt;strong&gt;0.17 seconds&lt;/strong&gt;, a &lt;strong&gt;6.5x improvement&lt;/strong&gt;, just by deferring one tool that I do not need on every shell session.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/fc47098a7d4a5b7e85229fd70a3ff7f3/04-zsh-after.gif&quot; alt=&quot;Fast zsh startup after lazy loading nvm&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;one-thing-to-watch-out-for&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#one-thing-to-watch-out-for&quot; aria-label=&quot;one thing to watch out for permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;One thing to watch out for&lt;/h2&gt;
&lt;p&gt;If you have scripts or tools that expect &lt;code class=&quot;language-text&quot;&gt;node&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; to be available immediately when the shell starts, lazy loading might cause issues. In my case, everything that runs Node goes through the terminal interactively, so it is fine. The first &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;node script.js&lt;/code&gt; takes a tiny bit longer because it loads nvm first, but after that, it is instant.&lt;/p&gt;
&lt;p&gt;If you use &lt;code class=&quot;language-text&quot;&gt;.nvmrc&lt;/code&gt; files and rely on nvm automatically switching Node versions when you &lt;code class=&quot;language-text&quot;&gt;cd&lt;/code&gt; into a project, you will need to trigger the load manually once, or add the auto-switching logic to the lazy loader. For my workflow, I just run &lt;code class=&quot;language-text&quot;&gt;nvm use&lt;/code&gt; when I need a specific version.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;1 second might not sound like much, but when you are opening terminals dozens of times a day, it adds up fast.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrade to Angular 20 from Angular 13 - Part 6: Angular 19 with Claude Code]]></title><link>https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part6-angular-19/</link><guid isPermaLink="false">https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part6-angular-19/</guid><pubDate>Sun, 22 Mar 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are back. In &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part5-angular-18/&quot;&gt;Part 5&lt;/a&gt;, we upgraded &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; from Angular 17 to 18 using Claude Code. That was the cleanup release: we removed all remaining NgModules, migrated to &lt;code class=&quot;language-text&quot;&gt;provideRouter()&lt;/code&gt; with standalone route configs, and upgraded Quill from v1 to v2.&lt;/p&gt;
&lt;p&gt;Angular 19 continues the theme of simplification. The headline change: &lt;code class=&quot;language-text&quot;&gt;standalone: true&lt;/code&gt; is now the default for all components, directives, and pipes. You no longer need to declare it. The flag we added to every single component during the Angular 17 migration? It can all be removed.&lt;/p&gt;
&lt;p&gt;Beyond that, Angular 19 requires TypeScript 5.5+ and zone.js 0.15. And the eslint tooling took a significant jump: &lt;code class=&quot;language-text&quot;&gt;@angular-eslint&lt;/code&gt; v19 requires &lt;code class=&quot;language-text&quot;&gt;typescript-eslint&lt;/code&gt; v8, up from v6.&lt;/p&gt;
&lt;h2 id=&quot;1-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-approach&quot; aria-label=&quot;1 approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Approach&lt;/h2&gt;
&lt;p&gt;Same pattern as every previous part. I gave Claude the previous implementation plans. It generated a &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/trung/v20-migration-part-6/docs/plans/2026-03-22-angular-19-upgrade.md&quot;&gt;plan&lt;/a&gt; with the usual structure: upgrade dependencies one at a time, verify builds, push and PR.&lt;/p&gt;
&lt;p&gt;The main difference from previous parts: there is no Phase 2. No code migration phase. Angular 19 has no new syntax to adopt, no modules to remove, no control flow to rewrite. The &lt;code class=&quot;language-text&quot;&gt;standalone: true&lt;/code&gt; removal is handled automatically by the &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt; schematic. This is the simplest upgrade in the series so far.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7f0a110b30cd4c0f418ab0fdb8006046/757ce/01-prompt.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAActUiSg//8QAFxAAAwEAAAAAAAAAAAAAAAAAABAhEf/aAAgBAQABBQKlWv8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEQESFB/9oACAEBAAE/IXjsNLFuP//aAAwDAQACAAMAAAAQkB//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAEAAQMFAAAAAAAAAAAAAAABABARQSExUWGB/9oACAEBAAE/EBsepbtFGWaQcUlXM//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude Code generating the 14-task migration plan&quot;
        title=&quot;Claude Code generating the 14-task migration plan&quot;
        src=&quot;/static/7f0a110b30cd4c0f418ab0fdb8006046/6a068/01-prompt.jpg&quot;
        srcset=&quot;/static/7f0a110b30cd4c0f418ab0fdb8006046/09b79/01-prompt.jpg 240w,
/static/7f0a110b30cd4c0f418ab0fdb8006046/7cc5e/01-prompt.jpg 480w,
/static/7f0a110b30cd4c0f418ab0fdb8006046/6a068/01-prompt.jpg 960w,
/static/7f0a110b30cd4c0f418ab0fdb8006046/644c5/01-prompt.jpg 1440w,
/static/7f0a110b30cd4c0f418ab0fdb8006046/0f98f/01-prompt.jpg 1920w,
/static/7f0a110b30cd4c0f418ab0fdb8006046/757ce/01-prompt.jpg 3118w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-dependency-upgrades&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-dependency-upgrades&quot; aria-label=&quot;2 dependency upgrades permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Dependency upgrades&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Before (v18)&lt;/th&gt;
&lt;th&gt;After (v19)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.14&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.2.20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.2.22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.0.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-eslint/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;18.4.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;19.8.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@typescript-eslint/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;6.21.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^8.0.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.14&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.2.19&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.3.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@ant-design/icons-angular&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^19.0.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^26.0.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^27.1.2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;typescript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~5.4.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~5.8.3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;zone.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~0.14.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~0.15.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt; schematic handled Angular core, TypeScript, and zone.js all in one step. That left the ecosystem dependencies to upgrade one at a time.&lt;/p&gt;
&lt;h3 id=&quot;21-standalone-true-removal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#21-standalone-true-removal&quot; aria-label=&quot;21 standalone true removal permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.1 &lt;code class=&quot;language-text&quot;&gt;standalone: true&lt;/code&gt; removal&lt;/h3&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;ng update @angular/core@19&lt;/code&gt; schematic automatically removes &lt;code class=&quot;language-text&quot;&gt;standalone: true&lt;/code&gt; from all component, directive, and pipe decorators. In Angular 19, standalone is the default. If you have a component that is NOT standalone (i.e., it belongs to an NgModule), the schematic adds &lt;code class=&quot;language-text&quot;&gt;standalone: false&lt;/code&gt; explicitly.&lt;/p&gt;
&lt;p&gt;In our case, all components are standalone. The schematic caught most of them. A few were missed (likely because they had slightly different decorator formatting). I removed the remaining ones manually. A quick &lt;code class=&quot;language-text&quot;&gt;grep -r &quot;standalone: true&quot; src/&lt;/code&gt; confirmed zero remaining.&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;board-dnd&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./board-dnd.component.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    styleUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./board-dnd.component.scss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    standalone&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;BoardDndListComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;board-dnd&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./board-dnd.component.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    styleUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./board-dnd.component.scss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;BoardDndListComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Less boilerplate. Every component file got one line shorter.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/73930a08b9e2b09557a965b044084049/02-standalone.gif&quot; alt=&quot;ng update schematic removing standalone: true from 38 components&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;22-typescript-eslint-v8-jump&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#22-typescript-eslint-v8-jump&quot; aria-label=&quot;22 typescript eslint v8 jump permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.2 &lt;code class=&quot;language-text&quot;&gt;typescript-eslint&lt;/code&gt; v8 jump&lt;/h3&gt;
&lt;p&gt;This was the riskiest dependency change. &lt;code class=&quot;language-text&quot;&gt;@angular-eslint&lt;/code&gt; v19 requires &lt;code class=&quot;language-text&quot;&gt;typescript-eslint&lt;/code&gt; v8, and we were on v6.21.0. That is a two-major-version jump.&lt;/p&gt;
&lt;p&gt;The good news: our &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; config is minimal with no custom rules. The upgrade went through cleanly. &lt;code class=&quot;language-text&quot;&gt;ng lint&lt;/code&gt; passed with zero errors on the first try. If your project has extensive custom eslint rules, you might have more work here.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5c10b9f9a36de73741e8f7abe8f49c25/b18ec/03-ts-eslint.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeWzCoP/xAAWEAADAAAAAAAAAAAAAAAAAAAAICH/2gAIAQEAAQUCIv8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGxAAAQUBAQAAAAAAAAAAAAAAAQAQESHwMaH/2gAIAQEAAT8hMYoU56qxaW//2gAMAwEAAgADAAAAECcP/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QWf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQACAwEAAAAAAAAAAAAAAAEAESExUXH/2gAIAQEAAT8QSNN+IdjXyRywMOS5bsF7P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ng lint passing with zero errors after typescript-eslint v8 upgrade&quot;
        title=&quot;ng lint passing with zero errors after typescript-eslint v8 upgrade&quot;
        src=&quot;/static/5c10b9f9a36de73741e8f7abe8f49c25/6a068/03-ts-eslint.jpg&quot;
        srcset=&quot;/static/5c10b9f9a36de73741e8f7abe8f49c25/09b79/03-ts-eslint.jpg 240w,
/static/5c10b9f9a36de73741e8f7abe8f49c25/7cc5e/03-ts-eslint.jpg 480w,
/static/5c10b9f9a36de73741e8f7abe8f49c25/6a068/03-ts-eslint.jpg 960w,
/static/5c10b9f9a36de73741e8f7abe8f49c25/644c5/03-ts-eslint.jpg 1440w,
/static/5c10b9f9a36de73741e8f7abe8f49c25/0f98f/03-ts-eslint.jpg 1920w,
/static/5c10b9f9a36de73741e8f7abe8f49c25/b18ec/03-ts-eslint.jpg 3094w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;23-browsertarget-rename&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#23-browsertarget-rename&quot; aria-label=&quot;23 browsertarget rename permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.3 &lt;code class=&quot;language-text&quot;&gt;browserTarget&lt;/code&gt; rename&lt;/h3&gt;
&lt;p&gt;One gotcha: Angular 19 renamed &lt;code class=&quot;language-text&quot;&gt;browserTarget&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;buildTarget&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt; serve configuration. The &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt; schematic did not catch this because we use &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack:dev-server&lt;/code&gt; instead of the standard builder. Running &lt;code class=&quot;language-text&quot;&gt;ng serve&lt;/code&gt; immediately failed with:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;plain&quot;&gt;&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;Error: Schema validation failed with the following errors:
  Data path &quot;&quot; must have required property &apos;buildTarget&apos;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A one-line fix in &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; &quot;browserTarget&quot;: &quot;frontend:build:development&quot;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &quot;buildTarget&quot;: &quot;frontend:build:development&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7b0c629f7e8ab06f3d2d855318aa2f0d/67226/04-build-target.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByogB/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAQABPyFmVz//2gAMAwEAAgADAAAAELcf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhAAAgIDAAAAAAAAAAAAAAAAABEBQRAhYf/aAAgBAQABPxBL2cSIpEFj/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude fixing the browserTarget to buildTarget rename in angular.json&quot;
        title=&quot;Claude fixing the browserTarget to buildTarget rename in angular.json&quot;
        src=&quot;/static/7b0c629f7e8ab06f3d2d855318aa2f0d/6a068/04-build-target.jpg&quot;
        srcset=&quot;/static/7b0c629f7e8ab06f3d2d855318aa2f0d/09b79/04-build-target.jpg 240w,
/static/7b0c629f7e8ab06f3d2d855318aa2f0d/7cc5e/04-build-target.jpg 480w,
/static/7b0c629f7e8ab06f3d2d855318aa2f0d/6a068/04-build-target.jpg 960w,
/static/7b0c629f7e8ab06f3d2d855318aa2f0d/644c5/04-build-target.jpg 1440w,
/static/7b0c629f7e8ab06f3d2d855318aa2f0d/0f98f/04-build-target.jpg 1920w,
/static/7b0c629f7e8ab06f3d2d855318aa2f0d/67226/04-build-target.jpg 3072w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;3-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-result&quot; aria-label=&quot;3 result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Result&lt;/h2&gt;
&lt;p&gt;Application compiles, builds, and lints without errors.&lt;/p&gt;
&lt;p&gt;Claude also opened Chrome and verified the app in the browser. This matters more than it sounds. An AI that can validate its own work end-to-end — not just compile, but actually click through the UI and check for runtime errors — can work independently without you babysitting each step.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9ecae3e35cb96348c26606112060ce35/394ef/05-verify.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHlRpAhh//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAICAwAAAAAAAAAAAAAAAAERABAhMVH/2gAIAQEAAT8hLVlLUxyv/9oADAMBAAIAAwAAABBTD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQEBAAMBAAAAAAAAAAAAAAEAESFRgbH/2gAIAQEAAT8Q4q/bW1gSu/Z6YsL/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude opening Chrome to verify the board view, issue detail, and settings page&quot;
        title=&quot;Claude opening Chrome to verify the board view, issue detail, and settings page&quot;
        src=&quot;/static/9ecae3e35cb96348c26606112060ce35/6a068/05-verify.jpg&quot;
        srcset=&quot;/static/9ecae3e35cb96348c26606112060ce35/09b79/05-verify.jpg 240w,
/static/9ecae3e35cb96348c26606112060ce35/7cc5e/05-verify.jpg 480w,
/static/9ecae3e35cb96348c26606112060ce35/6a068/05-verify.jpg 960w,
/static/9ecae3e35cb96348c26606112060ce35/644c5/05-verify.jpg 1440w,
/static/9ecae3e35cb96348c26606112060ce35/0f98f/05-verify.jpg 1920w,
/static/9ecae3e35cb96348c26606112060ce35/394ef/05-verify.jpg 3088w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;No runtime errors this time. Board view, issue detail with Quill editor, settings page, navigation — all working. The only console messages are pre-existing accessibility warnings about form labels.&lt;/p&gt;
&lt;h2 id=&quot;4-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-source-code&quot; aria-label=&quot;4 source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/113&quot;&gt;https://github.com/trungvose/jira-clone-angular/pull/113&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;5-what-i-learned&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-what-i-learned&quot; aria-label=&quot;5 what i learned permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. What I learned&lt;/h2&gt;
&lt;p&gt;Angular 19 is the quietest upgrade in this series. No new syntax to learn, no architecture to change, no surprising runtime errors. The &lt;code class=&quot;language-text&quot;&gt;standalone: true&lt;/code&gt; removal is satisfying but mechanical. The real work was already done in Angular 17 and 18.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;browserTarget&lt;/code&gt; → &lt;code class=&quot;language-text&quot;&gt;buildTarget&lt;/code&gt; rename is the kind of thing that bites you if you use custom builders. The standard Angular CLI schematics handle it automatically, but third-party builders like &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt; get left behind. Always test &lt;code class=&quot;language-text&quot;&gt;ng serve&lt;/code&gt; after an upgrade, even if &lt;code class=&quot;language-text&quot;&gt;ng build&lt;/code&gt; passes.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;typescript-eslint&lt;/code&gt; v6 → v8 jump looked scary but turned out to be painless for our minimal config. Your mileage may vary depending on how many custom lint rules you have.&lt;/p&gt;
&lt;p&gt;One observation across this entire series: each Angular upgrade has been simpler than the last. Part 1 (Angular 14) required manual dependency detective work. Part 4 (Angular 17) was a massive syntax and architecture migration. Part 5 (Angular 18) removed leftover modules. And now Part 6 (Angular 19) is essentially just bumping version numbers and removing a redundant flag. The Angular team’s incremental approach to modernization is working.&lt;/p&gt;
&lt;h2 id=&quot;6-whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-whats-next&quot; aria-label=&quot;6 whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. What’s next&lt;/h2&gt;
&lt;p&gt;We are at Angular 19. One more step to go: &lt;code class=&quot;language-text&quot;&gt;19 → 20&lt;/code&gt;. Angular 20 is the finish line. The codebase is now fully standalone, fully using modern control flow syntax, zero NgModules, and ready for whatever Angular 20 brings.&lt;/p&gt;
&lt;p&gt;See you in Part 7, the final part.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fast terminal navigation for running multiple AI agents in Antigravity]]></title><link>https://trungvose.comfast-terminal-navigation-ai-agents/</link><guid isPermaLink="false">https://trungvose.comfast-terminal-navigation-ai-agents/</guid><pubDate>Fri, 20 Mar 2026 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been using Claude Code extensively for the past few months. It changed how I work. Most of my coding now happens in the terminal, not in the editor. I write a prompt, Claude does the work, I review the diff. Repeat.&lt;/p&gt;
&lt;p&gt;Cursor is great, but it is built around the idea that AI lives inside the editor. Claude Code flips that. The terminal is the interface. Once I accepted that, I realized I needed a code editor that treats the terminal as a first-class citizen.&lt;/p&gt;
&lt;p&gt;That is when I switched to &lt;a href=&quot;https://antigravity.google/&quot;&gt;Antigravity&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;why-antigravity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-antigravity&quot; aria-label=&quot;why antigravity permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why Antigravity&lt;/h2&gt;
&lt;p&gt;Since I am using Claude Code for almost everything now, I do not need most of what Cursor offers. The tab completion with code suggestions? I do not use it. The inline prompt to generate code? I do not use that either. Everything goes through Claude Code in the terminal.&lt;/p&gt;
&lt;p&gt;So I asked myself, why am I paying for Cursor when I am not using any of its features?&lt;/p&gt;
&lt;p&gt;I wanted to switch back to something free, like VS Code. Then I found &lt;a href=&quot;https://antigravity.google/&quot;&gt;Antigravity&lt;/a&gt;. It is a good mix of what VS Code already does well, plus some promising features like Agent Manager. And it is free.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/000d54c9936fc9a97366df5d32c36f85/cecd0/01-antigravity.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAeStcuVKB//EABcQAAMBAAAAAAAAAAAAAAAAAAABERD/2gAIAQEAAQUCetEIf//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/Aar/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwGI/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRABAAIDAAAAAAAAAAAAAAAAABFBATGB/9oACAEBAAE/Iar26wSgg//aAAwDAQACAAMAAAAQ7D//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxCUv//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/ELW//8QAHBAAAgICAwAAAAAAAAAAAAAAAAERITGhQVFh/9oACAEBAAE/EHqDvkTcLEvuP4bL8bPPZ//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Antigravity&quot;
        title=&quot;Antigravity&quot;
        src=&quot;/static/000d54c9936fc9a97366df5d32c36f85/6a068/01-antigravity.jpg&quot;
        srcset=&quot;/static/000d54c9936fc9a97366df5d32c36f85/09b79/01-antigravity.jpg 240w,
/static/000d54c9936fc9a97366df5d32c36f85/7cc5e/01-antigravity.jpg 480w,
/static/000d54c9936fc9a97366df5d32c36f85/6a068/01-antigravity.jpg 960w,
/static/000d54c9936fc9a97366df5d32c36f85/644c5/01-antigravity.jpg 1440w,
/static/000d54c9936fc9a97366df5d32c36f85/0f98f/01-antigravity.jpg 1920w,
/static/000d54c9936fc9a97366df5d32c36f85/cecd0/01-antigravity.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;terminal-navigation-keybindings&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#terminal-navigation-keybindings&quot; aria-label=&quot;terminal navigation keybindings permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Terminal navigation keybindings&lt;/h2&gt;
&lt;p&gt;Here is the thing. If you are running multiple Claude Code sessions, or running your dev server in one tab and Claude in another, you need to switch between them quickly. Fumbling with the mouse to click on the right terminal tab breaks the flow.&lt;/p&gt;
&lt;p&gt;You could use iTerm separately from your IDE. It comes with fantastic shortcuts out of the box. But I prefer the integrated terminal within my IDE because I still need to open files and review diffs generated by Claude. Yes, I still review them.&lt;/p&gt;
&lt;p&gt;In the past, I usually had 2 or 3 terminals running at most. One for the API server, one for the frontend, and that was about it. No real need to navigate between them. But with Claude Code, I now have 4 to 5 terminals open at the same time. Fast navigation became a must.&lt;/p&gt;
&lt;p&gt;So I spent about an hour setting up these keybindings, and it made a huge difference. You can copy and paste this into your keybindings JSON file.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/91ca6c16ae5c91e2d16207be591fb65c/33774/02-config-keyboard.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGVWiocVCf/xAAaEAEAAgMBAAAAAAAAAAAAAAACAwQAARET/9oACAEBAAEFApJFp+ryukg63Vqp3IYPM//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EAB0QAAIBBAMAAAAAAAAAAAAAAAABEQIxMqESISL/2gAIAQEABj8Cq9u5mztu43z0Z6ImT//EABsQAQACAwEBAAAAAAAAAAAAAAEAESExQWHx/9oACAEBAAE/IcNILs+7HKr0zKAtuB6wC7Tep//aAAwDAQACAAMAAAAQmD//xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QTJ//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPxCn/8QAHhABAAIBBAMAAAAAAAAAAAAAAQARITFBUWFxkfD/2gAIAQEAAT8QKARAaMsz49+OeRl2bEMux0y1fMVbD73Aa7Nsdg56n//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Keyboard JSON&quot;
        title=&quot;Keyboard JSON&quot;
        src=&quot;/static/91ca6c16ae5c91e2d16207be591fb65c/6a068/02-config-keyboard.jpg&quot;
        srcset=&quot;/static/91ca6c16ae5c91e2d16207be591fb65c/09b79/02-config-keyboard.jpg 240w,
/static/91ca6c16ae5c91e2d16207be591fb65c/7cc5e/02-config-keyboard.jpg 480w,
/static/91ca6c16ae5c91e2d16207be591fb65c/6a068/02-config-keyboard.jpg 960w,
/static/91ca6c16ae5c91e2d16207be591fb65c/644c5/02-config-keyboard.jpg 1440w,
/static/91ca6c16ae5c91e2d16207be591fb65c/0f98f/02-config-keyboard.jpg 1920w,
/static/91ca6c16ae5c91e2d16207be591fb65c/33774/02-config-keyboard.jpg 2263w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here is the full keybindings config:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cmd+n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.terminal.new&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cmd+w&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.terminal.kill&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;alt+cmd+down&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.terminal.focusNext&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus &amp;amp;&amp;amp; terminalHasBeenCreated &amp;amp;&amp;amp; !terminalEditorFocus || terminalFocus &amp;amp;&amp;amp; terminalProcessSupported &amp;amp;&amp;amp; !terminalEditorFocus&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;alt+cmd+up&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.terminal.focusPrevious&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;alt+cmd+right&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.terminal.focusNextPane&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus &amp;amp;&amp;amp; terminalHasBeenCreated || terminalFocus &amp;amp;&amp;amp; terminalProcessSupported&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;alt+cmd+left&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.terminal.focusPreviousPane&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus &amp;amp;&amp;amp; terminalHasBeenCreated || terminalFocus &amp;amp;&amp;amp; terminalProcessSupported&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;shift+cmd+enter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.toggleMaximizedPanel&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;shift+cmd+=&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.increaseViewSize&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;shift+cmd+-&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;workbench.action.decreaseViewSize&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;when&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terminalFocus&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A quick breakdown:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⌘ N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New terminal tab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⌘ W&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Close terminal tab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⌥ ⌘ Down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Focus next terminal tab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⌥ ⌘ Up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Focus previous terminal tab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⌥ ⌘ Right&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Focus next pane (split window)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⌥ ⌘ Left&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Focus previous pane (split window)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⇧ ⌘ Enter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toggle maximize terminal panel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⇧ ⌘ =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Increase terminal size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;⇧ ⌘ -&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decrease terminal size&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The idea is simple.&lt;/p&gt;
&lt;h3 id=&quot;1-focus-into-the-terminal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-focus-into-the-terminal&quot; aria-label=&quot;1 focus into the terminal permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Focus into the terminal&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;⌘ J&lt;/code&gt; toggles the terminal panel. &lt;code class=&quot;language-text&quot;&gt;⌃ `&lt;/code&gt; opens a new terminal if none exists, or focuses into the existing one. These are default keybindings, no extra configuration needed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/299708a872d2bc0a7b6ca6e5722f5fa1/01-open-toggle.gif&quot; alt=&quot;Focus into the terminal&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-open-and-close-terminal-tabs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-open-and-close-terminal-tabs&quot; aria-label=&quot;2 open and close terminal tabs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Open and close terminal tabs&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;⌘ N&lt;/code&gt; creates a new terminal tab and &lt;code class=&quot;language-text&quot;&gt;⌘ W&lt;/code&gt; closes the current one, just like managing tabs in a browser.&lt;/p&gt;
&lt;p&gt;Note that the terminal &lt;strong&gt;must be focused&lt;/strong&gt; for these to work. Otherwise, &lt;code class=&quot;language-text&quot;&gt;⌘ W&lt;/code&gt; will close the editor tab, or Antigravity itself if no tab is open.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/49b0dc4a2b837eb74164047d6e11aad8/02-new-close.gif&quot; alt=&quot;Open and close terminal tabs&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-navigate-between-tabs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-navigate-between-tabs&quot; aria-label=&quot;3 navigate between tabs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Navigate between tabs&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;⌥ ⌘ Up/Down&lt;/code&gt; moves between terminal tabs. The tabs are arranged vertically in the UI, so up and down feels natural to remember.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/3a6a52fcbe83177975673857337d72bc/03-navigate-tabs.gif&quot; alt=&quot;Navigate between terminal tabs&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;4-navigate-between-panes-within-a-tab&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-navigate-between-panes-within-a-tab&quot; aria-label=&quot;4 navigate between panes within a tab permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Navigate between panes within a tab&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;⌥ ⌘ Left/Right&lt;/code&gt; moves between split panes within a single tab. Useful when you have Claude Code running on one side and your dev server on the other.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d95ba21d32ada64ecd15d5fc3d4e4570/04-navigate-windows.gif&quot; alt=&quot;Navigate between panes within a tab&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;5-resize-and-maximize&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-resize-and-maximize&quot; aria-label=&quot;5 resize and maximize permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Resize and maximize&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;⇧ ⌘ Enter&lt;/code&gt; toggles the terminal panel to full screen. &lt;code class=&quot;language-text&quot;&gt;⇧ ⌘ =&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;⇧ ⌘ -&lt;/code&gt; increase and decrease the terminal size.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/ec03519125b7f3272839823e6424328c/05-resize.gif&quot; alt=&quot;Resize and maximize terminal&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once your fingers learn these shortcuts, navigating between multiple terminal sessions becomes second nature. When your workflow is terminal-first with Claude Code, that speed matters.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrade to Angular 20 from Angular 13 - Part 5: Angular 18 with Claude Code]]></title><link>https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part5-angular-18/</link><guid isPermaLink="false">https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part5-angular-18/</guid><pubDate>Sat, 14 Mar 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are back. In &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part4-angular-17/&quot;&gt;Part 4&lt;/a&gt;, we upgraded &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; from Angular 16 to 17 using Claude Code. That was the big one: standalone components, new control flow syntax, standalone bootstrap. A lot of code changed, but the Angular CLI schematics did most of the heavy lifting.&lt;/p&gt;
&lt;p&gt;Angular 18 is a different kind of upgrade. No new syntax. No big rewrite. Instead, this release is about finishing the cleanup that Angular 17 started. The standalone migration left behind six NgModule wrappers that were kept as lazy-loading entry points. Angular 18 gave us the reason and the tools to finally remove them all.&lt;/p&gt;
&lt;p&gt;The other notable change: Quill v1 to v2. Not because Angular 18 requires it, but because ngx-quill 26+ (the only version that supports Angular 18) dropped support for Quill v1. So we had to upgrade both at the same time.&lt;/p&gt;
&lt;h2 id=&quot;1-the-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-the-approach&quot; aria-label=&quot;1 the approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. The approach&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/26d730e9b0acc7696322127d195c3e95/4793e/initial-prompt.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcyZBiD/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAIDH/2gAIAQEABj8CIv8A/8QAGhAAAgIDAAAAAAAAAAAAAAAAABEBECFhgf/aAAgBAQABPyHhOA9Dv//aAAwDAQACAAMAAAAQ8w//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAgMBAAAAAAAAAAAAAAABAHEQEVEx/9oACAEBAAE/EHaea0xcSyPIKm8Wsf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The approach&quot;
        title=&quot;The approach&quot;
        src=&quot;/static/26d730e9b0acc7696322127d195c3e95/6a068/initial-prompt.jpg&quot;
        srcset=&quot;/static/26d730e9b0acc7696322127d195c3e95/09b79/initial-prompt.jpg 240w,
/static/26d730e9b0acc7696322127d195c3e95/7cc5e/initial-prompt.jpg 480w,
/static/26d730e9b0acc7696322127d195c3e95/6a068/initial-prompt.jpg 960w,
/static/26d730e9b0acc7696322127d195c3e95/644c5/initial-prompt.jpg 1440w,
/static/26d730e9b0acc7696322127d195c3e95/0f98f/initial-prompt.jpg 1920w,
/static/26d730e9b0acc7696322127d195c3e95/4793e/initial-prompt.jpg 2051w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Same pattern as before. I gave Claude the previous blog posts, the implementation plan from &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part4-angular-17/&quot;&gt;Part 4&lt;/a&gt;, and the current state of the codebase. It generated a &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/trung/v20-migration-part-5/docs/plans/2026-03-12-angular-18-upgrade.md&quot;&gt;plan&lt;/a&gt; with two phases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Phase 1: Dependency upgrades&lt;/strong&gt; - the same incremental pattern from Parts 1-4&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2: NgModule cleanup&lt;/strong&gt; - remove all remaining NgModules, migrate to &lt;code class=&quot;language-text&quot;&gt;provideRouter()&lt;/code&gt; with standalone route configs&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;2-phase-1-dependency-upgrades&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-phase-1-dependency-upgrades&quot; aria-label=&quot;2 phase 1 dependency upgrades permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Phase 1: Dependency upgrades&lt;/h2&gt;
&lt;p&gt;Familiar territory by now. Run &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt;, then resolve each dependency one at a time, commit after each step.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Before (v17)&lt;/th&gt;
&lt;th&gt;After (v18)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.3.12&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.14&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.3.17&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.21&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.0.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.0.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-eslint/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;17.5.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;18.4.3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.3.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.14&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.4.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.2.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@ant-design/icons-angular&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^18.0.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^24.0.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^26.0.10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;quill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^1.3.7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^2.0.3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;TypeScript (&lt;code class=&quot;language-text&quot;&gt;~5.4.5&lt;/code&gt;) and zone.js (&lt;code class=&quot;language-text&quot;&gt;~0.14.10&lt;/code&gt;) were already compatible with Angular 18, so no changes needed there.&lt;/p&gt;
&lt;h3 id=&quot;21-the-quill-v2-migration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#21-the-quill-v2-migration&quot; aria-label=&quot;21 the quill v2 migration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.1 The Quill v2 migration&lt;/h3&gt;
&lt;p&gt;In &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part4-angular-17/&quot;&gt;Part 4&lt;/a&gt;, I specifically called out that we stayed on ngx-quill v24 to avoid the Quill v2 migration. That is over now. ngx-quill 26+ is the only version that supports Angular 18, and it requires Quill v2.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5e7effd11c3cc8731bfe0346c4987397/57a9c/quill-v2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAIDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAHNiJrCv//EABYQAAMAAAAAAAAAAAAAAAAAAAEQMf/aAAgBAQABBQI1f//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABgQAQEAAwAAAAAAAAAAAAAAAAEAEUFR/9oACAEBAAE/IU7Mr2zf/9oADAMBAAIAAwAAABCED//EABYRAAMAAAAAAAAAAAAAAAAAAAEQMf/aAAgBAwEBPxA1f//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAAEEAwAAAAAAAAAAAAAAAAEAESFRQWGR/9oACAEBAAE/EH4RNekYJIwcp1nq/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Quill v2 migration&quot;
        title=&quot;Quill v2 migration&quot;
        src=&quot;/static/5e7effd11c3cc8731bfe0346c4987397/6a068/quill-v2.jpg&quot;
        srcset=&quot;/static/5e7effd11c3cc8731bfe0346c4987397/09b79/quill-v2.jpg 240w,
/static/5e7effd11c3cc8731bfe0346c4987397/7cc5e/quill-v2.jpg 480w,
/static/5e7effd11c3cc8731bfe0346c4987397/6a068/quill-v2.jpg 960w,
/static/5e7effd11c3cc8731bfe0346c4987397/644c5/quill-v2.jpg 1440w,
/static/5e7effd11c3cc8731bfe0346c4987397/0f98f/quill-v2.jpg 1920w,
/static/5e7effd11c3cc8731bfe0346c4987397/57a9c/quill-v2.jpg 2094w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The good news: our Quill usage is minimal. Two editor instances (issue description and create issue modal) with a standard toolbar config. No custom modules, no custom formats.&lt;/p&gt;
&lt;p&gt;The only concrete change was in &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt;. Quill v2 does not ship &lt;code class=&quot;language-text&quot;&gt;quill.min.js&lt;/code&gt; - the minified bundle simply does not exist in the v2 distribution. So the scripts entry needed to change:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; &quot;./node_modules/quill/dist/quill.min.js&quot;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &quot;./node_modules/quill/dist/quill.js&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The CSS files (&lt;code class=&quot;language-text&quot;&gt;quill.core.css&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;quill.snow.css&lt;/code&gt;) kept their paths. The toolbar configuration and &lt;code class=&quot;language-text&quot;&gt;&amp;lt;quill-editor&gt;&lt;/code&gt; component API were fully compatible. No template or component changes needed.&lt;/p&gt;
&lt;p&gt;After all dependency upgrades, &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; ran cleanly without &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt;. Build passed on the first try.&lt;/p&gt;
&lt;h2 id=&quot;3-phase-2-ngmodule-cleanup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-phase-2-ngmodule-cleanup&quot; aria-label=&quot;3 phase 2 ngmodule cleanup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Phase 2: NgModule cleanup&lt;/h2&gt;
&lt;p&gt;This is the part that made Angular 18 worth writing about.&lt;/p&gt;
&lt;p&gt;After the Angular 17 standalone migration, several NgModule files were left behind. They were not doing anything meaningful, they were just wrappers for routing modules, lazy-loaded feature modules, and barrel re-exports.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;AppRoutingModule&lt;/code&gt;&lt;/strong&gt; - wrapping &lt;code class=&quot;language-text&quot;&gt;RouterModule.forRoot(routes)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;ProjectModule&lt;/code&gt;&lt;/strong&gt; - importing standalone components and ng-zorro modules&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;ProjectRoutingModule&lt;/code&gt;&lt;/strong&gt; - wrapping &lt;code class=&quot;language-text&quot;&gt;RouterModule.forChild(routes)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;WorkInProgressModule&lt;/code&gt;&lt;/strong&gt; - wrapping a single standalone component&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;WorkInProgressRoutingModule&lt;/code&gt;&lt;/strong&gt; - wrapping a single route&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;JiraControlModule&lt;/code&gt;&lt;/strong&gt; - re-exporting standalone components as a barrel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The reason they survived &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part4-angular-17/&quot;&gt;Part 4&lt;/a&gt;: Angular’s lazy loading used &lt;code class=&quot;language-text&quot;&gt;loadChildren&lt;/code&gt; with module references, and the standalone schematics did not convert these automatically.&lt;/p&gt;
&lt;h3 id=&quot;31-replacing-modules-with-route-configs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31-replacing-modules-with-route-configs&quot; aria-label=&quot;31 replacing modules with route configs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1 Replacing modules with route configs&lt;/h3&gt;
&lt;p&gt;The fix is straightforward. Instead of wrapping routes in a NgModule with &lt;code class=&quot;language-text&quot;&gt;RouterModule.forChild()&lt;/code&gt;, export a plain &lt;code class=&quot;language-text&quot;&gt;Routes&lt;/code&gt; array from a file. Then in the parent route config, use &lt;code class=&quot;language-text&quot;&gt;loadChildren&lt;/code&gt; to point at that file instead of the old module:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before: app-routing.module.ts&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;project&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;loadChildren&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./project/project.module&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ProjectModule&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After: app.routes.ts&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;project&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;loadChildren&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./project/project.routes&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PROJECT_ROUTES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The child route file itself becomes a simple export with no NgModule wrapper:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before: project-routing.module.ts&lt;/span&gt;
&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;RouterModule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;routes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  exports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;RouterModule&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProjectRoutingModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After: project.routes.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PROJECT_ROUTES&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Routes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    providers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;importProvidersFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;NzIconModule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NZ_JIRA_ICONS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    children&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;board&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; BoardComponent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;settings&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SettingsComponent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;issue/:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;ProjectConst&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IssueId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FullIssueDetailComponent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; redirectTo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;board&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pathMatch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;full&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key insight: &lt;code class=&quot;language-text&quot;&gt;loadChildren&lt;/code&gt; in Angular 18 can resolve either a module or a plain &lt;code class=&quot;language-text&quot;&gt;Routes&lt;/code&gt; array. No &lt;code class=&quot;language-text&quot;&gt;RouterModule.forChild()&lt;/code&gt; boilerplate needed. The &lt;code class=&quot;language-text&quot;&gt;NzIconModule.forChild(NZ_JIRA_ICONS)&lt;/code&gt; is preserved via route-level &lt;code class=&quot;language-text&quot;&gt;providers&lt;/code&gt;, ensuring the ng-zorro icons are still registered when the project feature loads.&lt;/p&gt;
&lt;h3 id=&quot;32-replacing-approutingmodule-with-providerouter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#32-replacing-approutingmodule-with-providerouter&quot; aria-label=&quot;32 replacing approutingmodule with providerouter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.2 Replacing AppRoutingModule with provideRouter&lt;/h3&gt;
&lt;p&gt;In &lt;code class=&quot;language-text&quot;&gt;main.ts&lt;/code&gt;, the &lt;code class=&quot;language-text&quot;&gt;AppRoutingModule&lt;/code&gt; inside &lt;code class=&quot;language-text&quot;&gt;importProvidersFrom()&lt;/code&gt; was replaced with Angular’s &lt;code class=&quot;language-text&quot;&gt;provideRouter()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;importProvidersFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;BrowserModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ReactiveFormsModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; AppRoutingModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;provideRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;appRoutes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;importProvidersFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;NzIconModule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; AkitaNgRouterStoreModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; QuillModule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While we were at it, we also cleaned up &lt;code class=&quot;language-text&quot;&gt;main.ts&lt;/code&gt; by removing &lt;code class=&quot;language-text&quot;&gt;BrowserModule&lt;/code&gt; (automatically included by &lt;code class=&quot;language-text&quot;&gt;bootstrapApplication&lt;/code&gt;), &lt;code class=&quot;language-text&quot;&gt;ReactiveFormsModule&lt;/code&gt; (already imported by each standalone component that uses forms), and &lt;code class=&quot;language-text&quot;&gt;NzSpinModule&lt;/code&gt; (already imported directly in &lt;code class=&quot;language-text&quot;&gt;AppComponent&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&quot;4-the-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-the-result&quot; aria-label=&quot;4 the result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. The result&lt;/h2&gt;
&lt;p&gt;Application compiles, builds, and lints without errors. The lazy chunk names changed from &lt;code class=&quot;language-text&quot;&gt;project-project-module&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;project-project-routes&lt;/code&gt;, confirming the module-free routing is working.&lt;/p&gt;
&lt;h2 id=&quot;5-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-source-code&quot; aria-label=&quot;5 source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/111&quot;&gt;https://github.com/trungvose/jira-clone-angular/pull/111&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;6-the-hidden-gotcha-service-providers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-the-hidden-gotcha-service-providers&quot; aria-label=&quot;6 the hidden gotcha service providers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. The hidden gotcha: service providers&lt;/h2&gt;
&lt;p&gt;Everything compiled. The build passed. Then I opened the app and hit this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;NullInjectorError: No provider for NzDrawerService!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/853c9633985929ef83a47fa6f8cef110/cecd0/NullInjectorError.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHvZSJUP//EABYQAQEBAAAAAAAAAAAAAAAAAAARIP/aAAgBAQABBQJEx//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABcQAAMBAAAAAAAAAAAAAAAAAAABESD/2gAIAQEAAT8hapJGP//aAAwDAQACAAMAAAAQqz//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAACAgMAAAAAAAAAAAAAAAAAARARQWGR/9oACAEBAAE/EFJTFfnpoHP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The hidden gotcha: service providers&quot;
        title=&quot;The hidden gotcha: service providers&quot;
        src=&quot;/static/853c9633985929ef83a47fa6f8cef110/6a068/NullInjectorError.jpg&quot;
        srcset=&quot;/static/853c9633985929ef83a47fa6f8cef110/09b79/NullInjectorError.jpg 240w,
/static/853c9633985929ef83a47fa6f8cef110/7cc5e/NullInjectorError.jpg 480w,
/static/853c9633985929ef83a47fa6f8cef110/6a068/NullInjectorError.jpg 960w,
/static/853c9633985929ef83a47fa6f8cef110/644c5/NullInjectorError.jpg 1440w,
/static/853c9633985929ef83a47fa6f8cef110/0f98f/NullInjectorError.jpg 1920w,
/static/853c9633985929ef83a47fa6f8cef110/cecd0/NullInjectorError.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This is the part that catches you if you are not careful. NgModules do two things: they declare/import components, and they &lt;strong&gt;register services&lt;/strong&gt;. When &lt;code class=&quot;language-text&quot;&gt;ProjectModule&lt;/code&gt; imported &lt;code class=&quot;language-text&quot;&gt;NzDrawerModule&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;NzModalModule&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;NzNotificationModule&lt;/code&gt;, those modules registered their services (&lt;code class=&quot;language-text&quot;&gt;NzDrawerService&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;NzModalService&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;NzNotificationService&lt;/code&gt;) in the module’s injector. Standalone components import the &lt;em&gt;component&lt;/em&gt; directives directly, but the &lt;em&gt;services&lt;/em&gt; still need a provider somewhere.&lt;/p&gt;
&lt;p&gt;The fix was to add those modules to the route-level &lt;code class=&quot;language-text&quot;&gt;providers&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PROJECT_ROUTES&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Routes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    providers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;importProvidersFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        NzIconModule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NZ_JIRA_ICONS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        NzDrawerModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        NzModalModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        NzNotificationModule
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a good reminder that &lt;code class=&quot;language-text&quot;&gt;ng build&lt;/code&gt; passing does not mean the app works. The compiler cannot catch missing providers. Those are &lt;code class=&quot;language-text&quot;&gt;runtime errors&lt;/code&gt;. If you are removing NgModules, test the actual features that depend on injected services from those modules. The build will not save you here.&lt;/p&gt;
&lt;p&gt;I had already merged the &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/111&quot;&gt;main PR (#111)&lt;/a&gt; before catching this, so it went out as a &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/112&quot;&gt;follow-up hotfix (#112)&lt;/a&gt;. Fortunately I had not yet published this build on Netlify, so no users were affected.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/95d8a7c44e0f6a425fff2ec069f0c219/90683/netlify-deploy.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQACBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7oQiP/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAADAAMAAAAAAAAAAAAAAAAAARAhMUH/2gAIAQEAAT8hWzMXYj//2gAMAwEAAgADAAAAEIPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxAAAgIDAQAAAAAAAAAAAAAAAAERMRBBcbH/2gAIAQEAAT8QZyLagW+ntir6f//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Netlify deployment&quot;
        title=&quot;Netlify deployment&quot;
        src=&quot;/static/95d8a7c44e0f6a425fff2ec069f0c219/6a068/netlify-deploy.jpg&quot;
        srcset=&quot;/static/95d8a7c44e0f6a425fff2ec069f0c219/09b79/netlify-deploy.jpg 240w,
/static/95d8a7c44e0f6a425fff2ec069f0c219/7cc5e/netlify-deploy.jpg 480w,
/static/95d8a7c44e0f6a425fff2ec069f0c219/6a068/netlify-deploy.jpg 960w,
/static/95d8a7c44e0f6a425fff2ec069f0c219/644c5/netlify-deploy.jpg 1440w,
/static/95d8a7c44e0f6a425fff2ec069f0c219/0f98f/netlify-deploy.jpg 1920w,
/static/95d8a7c44e0f6a425fff2ec069f0c219/90683/netlify-deploy.jpg 2842w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;7-what-i-learned&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-what-i-learned&quot; aria-label=&quot;7 what i learned permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. What I learned&lt;/h2&gt;
&lt;p&gt;Angular 18 is the cleanup release. The hard architectural work was done in Angular 17. This release is about removing the leftover wrappers.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e780136bf0712dd50e4c068f55effafc/cecd0/angular-18.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2VMMo//EABYQAAMAAAAAAAAAAAAAAAAAAAIQMv/aAAgBAQABBQIZQQv/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAABEHH/2gAIAQEABj8CEYj/AP/EABoQAQABBQAAAAAAAAAAAAAAAAChARAhQZH/2gAIAQEAAT8hjlcuIJu3/9oADAMBAAIAAwAAABDID//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ECf/xAAWEQADAAAAAAAAAAAAAAAAAAABEDH/2gAIAQIBAT8QNX//xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhMUFR8P/aAAgBAQABPxAK+2xfcQWEoxmUdJHycIEO5//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 18 live&quot;
        title=&quot;Angular 18 live&quot;
        src=&quot;/static/e780136bf0712dd50e4c068f55effafc/6a068/angular-18.jpg&quot;
        srcset=&quot;/static/e780136bf0712dd50e4c068f55effafc/09b79/angular-18.jpg 240w,
/static/e780136bf0712dd50e4c068f55effafc/7cc5e/angular-18.jpg 480w,
/static/e780136bf0712dd50e4c068f55effafc/6a068/angular-18.jpg 960w,
/static/e780136bf0712dd50e4c068f55effafc/644c5/angular-18.jpg 1440w,
/static/e780136bf0712dd50e4c068f55effafc/0f98f/angular-18.jpg 1920w,
/static/e780136bf0712dd50e4c068f55effafc/cecd0/angular-18.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The NgModule removal was the most satisfying part. Those six module files were boilerplate that existed only because the lazy loading system used to require them. Replacing them with plain route config arrays made the codebase simpler and easier to understand. No more &lt;code class=&quot;language-text&quot;&gt;RouterModule.forChild()&lt;/code&gt; boilerplate, no more barrel re-export modules.&lt;/p&gt;
&lt;p&gt;The Quill v1 to v2 migration was the biggest risk on paper, but turned out to be a non-issue. For basic toolbar usage, the API is compatible. The only breaking change we hit was a missing file (&lt;code class=&quot;language-text&quot;&gt;quill.min.js&lt;/code&gt; renamed to &lt;code class=&quot;language-text&quot;&gt;quill.js&lt;/code&gt;). If your project uses custom Quill modules or formats, your experience would likely be different.&lt;/p&gt;
&lt;p&gt;One small thing worth noting: &lt;code class=&quot;language-text&quot;&gt;extractCss: true&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt; is deprecated in Angular 18. The &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt; schematic did not remove it automatically, but the build still passed. We removed it manually to stay clean.&lt;/p&gt;
&lt;h2 id=&quot;8-whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#8-whats-next&quot; aria-label=&quot;8 whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. What’s next&lt;/h2&gt;
&lt;p&gt;We are at Angular 18. The remaining path is &lt;code class=&quot;language-text&quot;&gt;18 -&gt; 19 -&gt; 20&lt;/code&gt;. Angular 19 introduces the new signal-based forms and makes zoneless change detection more accessible. The codebase is now fully standalone with no NgModules, which puts us in a good position for the signal-based future.&lt;/p&gt;
&lt;p&gt;See you in Part 6.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrade to Angular 20 from Angular 13 - Part 4: Angular 17 with Claude Code]]></title><link>https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part4-angular-17/</link><guid isPermaLink="false">https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part4-angular-17/</guid><pubDate>Thu, 12 Mar 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are back. In &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part3-angular-16/&quot;&gt;Part 3&lt;/a&gt;, we upgraded &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; from Angular 15 to 16 using Claude Code, and the whole thing was mostly just dependency bumps. I mentioned at the end that Angular 17 would be the interesting one. It was.&lt;/p&gt;
&lt;p&gt;Angular 17 is not just another version bump. This is the release where standalone components become the default, the new control flow syntax (&lt;code class=&quot;language-text&quot;&gt;@if&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@for&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@switch&lt;/code&gt;) replaces structural directives, and there is an entirely new build system available. For the first time in this series, we need actual code changes, not just &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; updates.&lt;/p&gt;
&lt;h2 id=&quot;1-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-approach&quot; aria-label=&quot;1 approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Approach&lt;/h2&gt;
&lt;p&gt;Same as &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part3-angular-16/&quot;&gt;Part 3&lt;/a&gt;. I gave Claude the previous blog posts and the implementation plan from the Angular 16 upgrade, and asked it to generate a new plan for Angular 17.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f2f0cc421a48bcb81cc11828ad54d872/3d027/angular-17-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMCBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVmkgD/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAEAAwEAAAAAAAAAAAAAAAABABARIf/aAAgBAQABPyHHI0nL/9oADAMBAAIAAwAAABBAD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB0QAQACAQUBAAAAAAAAAAAAAAEAESExQVFxkYH/2gAIAQEAAT8QaGGzN8wI7HyLcLxL68jrP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Convert to standalone components&quot;
        title=&quot;Convert to standalone components&quot;
        src=&quot;/static/f2f0cc421a48bcb81cc11828ad54d872/6a068/angular-17-01.jpg&quot;
        srcset=&quot;/static/f2f0cc421a48bcb81cc11828ad54d872/09b79/angular-17-01.jpg 240w,
/static/f2f0cc421a48bcb81cc11828ad54d872/7cc5e/angular-17-01.jpg 480w,
/static/f2f0cc421a48bcb81cc11828ad54d872/6a068/angular-17-01.jpg 960w,
/static/f2f0cc421a48bcb81cc11828ad54d872/644c5/angular-17-01.jpg 1440w,
/static/f2f0cc421a48bcb81cc11828ad54d872/0f98f/angular-17-01.jpg 1920w,
/static/f2f0cc421a48bcb81cc11828ad54d872/3d027/angular-17-01.jpg 2346w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The difference this time is that the plan had two phases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Phase 1: Dependency upgrades&lt;/strong&gt; - the same proven pattern from Parts 1-3&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2: Code migration&lt;/strong&gt; - standalone components, standalone bootstrap, and new control flow syntax&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Claude produced a plan covering everything from the initial &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt; all the way to creating the PR. The full plan is available in the &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/trung/v20-migration-part-4/docs/plans/2026-03-11-angular-17-upgrade.md&quot;&gt;repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;2-phase-1-dependency-upgrades&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-phase-1-dependency-upgrades&quot; aria-label=&quot;2 phase 1 dependency upgrades permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Phase 1: Dependency upgrades&lt;/h2&gt;
&lt;p&gt;This part was familiar. Same pattern as before:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code class=&quot;language-text&quot;&gt;ng update @angular/core@17 @angular/cli@17 --force&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Resolve each dependency conflict one at a time&lt;/li&gt;
&lt;li&gt;Commit after each step&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One nice thing: &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt; automatically bumped TypeScript to 5.4.5 and zone.js to 0.14.10 this time, so two tasks from the plan were already done.&lt;/p&gt;
&lt;p&gt;The remaining dependencies followed the usual flow:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Before (v16)&lt;/th&gt;
&lt;th&gt;After (v17)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.12&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.3.12&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.3.17&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;typescript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~5.1.6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~5.4.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;zone.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~0.13.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~0.14.10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.4.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^22.1.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^24.0.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.14&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^17.3.10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-eslint/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;16.3.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;17.5.3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@typescript-eslint/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;5.11.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;6.21.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For ngx-quill, it was important to stay on v24.x and not jump to v25+. Version 25 requires Quill v2, which is a completely different API. Version 24 supports Angular 17 while keeping Quill v1. That saved us from a much bigger migration.&lt;/p&gt;
&lt;p&gt;After resolving all dependencies, &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; ran cleanly without &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt;. Build passed on the first try.&lt;/p&gt;
&lt;h2 id=&quot;3-phase-2-the-real-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-phase-2-the-real-work&quot; aria-label=&quot;3 phase 2 the real work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Phase 2: The real work&lt;/h2&gt;
&lt;p&gt;This is where it got interesting. Angular 17 provides CLI schematics to automate the three major code migrations:&lt;/p&gt;
&lt;h3 id=&quot;31-convert-to-standalone-components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31-convert-to-standalone-components&quot; aria-label=&quot;31 convert to standalone components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1 Convert to standalone components&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng generate @angular/core:standalone &lt;span class=&quot;token parameter variable&quot;&gt;--mode&lt;/span&gt; convert-to-standalone&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This schematic went through all components and directives, added &lt;code class=&quot;language-text&quot;&gt;standalone: true&lt;/code&gt; to each one, and moved the necessary imports from their parent NgModules into each component’s own &lt;code class=&quot;language-text&quot;&gt;imports&lt;/code&gt; array.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e6ab1079a3b7638d2a364e9ef6c664d2/48eb7/angular-17-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByywGo//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAAIBBQAAAAAAAAAAAAAAAAABERAxYaHw/9oACAEBAAE/IZ6B4Wqu4z//2gAMAwEAAgADAAAAEAAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGBABAQEBAQAAAAAAAAAAAAAAAQAxEUH/2gAIAQEAAT8QQHNqZI+jNpav/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Convert to standalone components&quot;
        title=&quot;Convert to standalone components&quot;
        src=&quot;/static/e6ab1079a3b7638d2a364e9ef6c664d2/6a068/angular-17-02.jpg&quot;
        srcset=&quot;/static/e6ab1079a3b7638d2a364e9ef6c664d2/09b79/angular-17-02.jpg 240w,
/static/e6ab1079a3b7638d2a364e9ef6c664d2/7cc5e/angular-17-02.jpg 480w,
/static/e6ab1079a3b7638d2a364e9ef6c664d2/6a068/angular-17-02.jpg 960w,
/static/e6ab1079a3b7638d2a364e9ef6c664d2/644c5/angular-17-02.jpg 1440w,
/static/e6ab1079a3b7638d2a364e9ef6c664d2/0f98f/angular-17-02.jpg 1920w,
/static/e6ab1079a3b7638d2a364e9ef6c664d2/48eb7/angular-17-02.jpg 2334w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;32-prune-ngmodules&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#32-prune-ngmodules&quot; aria-label=&quot;32 prune ngmodules permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.2 Prune NgModules&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng generate @angular/core:standalone &lt;span class=&quot;token parameter variable&quot;&gt;--mode&lt;/span&gt; prune-ng-modules&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This removed NgModule classes that were no longer needed. In our case, only &lt;code class=&quot;language-text&quot;&gt;SnowModule&lt;/code&gt; was fully pruned. The other modules like &lt;code class=&quot;language-text&quot;&gt;ProjectModule&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;WorkInProgressModule&lt;/code&gt; were kept because they still serve as entry points for lazy-loaded routes.&lt;/p&gt;
&lt;p&gt;There was one issue here. The schematic deleted &lt;code class=&quot;language-text&quot;&gt;SnowModule&lt;/code&gt; but forgot to add &lt;code class=&quot;language-text&quot;&gt;SnowComponent&lt;/code&gt; to the imports of &lt;code class=&quot;language-text&quot;&gt;AppModule&lt;/code&gt;. The build failed with &lt;code class=&quot;language-text&quot;&gt;&apos;j-snow&apos; is not a known element&lt;/code&gt;. Quick fix, added the import manually.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dde6a5be0bbd20403e4300c2b391e399/54c7d/angular-17-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeXIMg//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAACAgMAAAAAAAAAAAAAAAAAARExECFR/9oACAEBAAE/IY1aGo4Md4Z//9oADAMBAAIAAwAAABCgz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAAMBAAMAAAAAAAAAAAAAAAABMRFhcbH/2gAIAQEAAT8Q06FykbF4RRTsVKP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Prune NgModules&quot;
        title=&quot;Prune NgModules&quot;
        src=&quot;/static/dde6a5be0bbd20403e4300c2b391e399/6a068/angular-17-03.jpg&quot;
        srcset=&quot;/static/dde6a5be0bbd20403e4300c2b391e399/09b79/angular-17-03.jpg 240w,
/static/dde6a5be0bbd20403e4300c2b391e399/7cc5e/angular-17-03.jpg 480w,
/static/dde6a5be0bbd20403e4300c2b391e399/6a068/angular-17-03.jpg 960w,
/static/dde6a5be0bbd20403e4300c2b391e399/644c5/angular-17-03.jpg 1440w,
/static/dde6a5be0bbd20403e4300c2b391e399/0f98f/angular-17-03.jpg 1920w,
/static/dde6a5be0bbd20403e4300c2b391e399/54c7d/angular-17-03.jpg 2069w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;33-migrate-to-standalone-bootstrap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#33-migrate-to-standalone-bootstrap&quot; aria-label=&quot;33 migrate to standalone bootstrap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.3 Migrate to standalone bootstrap&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng generate @angular/core:standalone &lt;span class=&quot;token parameter variable&quot;&gt;--mode&lt;/span&gt; standalone-bootstrap&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This was the big one. It converted &lt;code class=&quot;language-text&quot;&gt;main.ts&lt;/code&gt; from:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;platformBrowserDynamic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bootstrapModule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AppModule&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;bootstrapApplication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AppComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  providers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;importProvidersFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;BrowserModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; AppRoutingModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;provideAnimations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;provideHttpClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withInterceptorsFromDi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Sentry, Akita, and other providers&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then deleted &lt;code class=&quot;language-text&quot;&gt;AppModule&lt;/code&gt; entirely. The schematic handled most of it correctly, but missed the &lt;code class=&quot;language-text&quot;&gt;import * as Sentry&lt;/code&gt; statement that was previously in &lt;code class=&quot;language-text&quot;&gt;app.module.ts&lt;/code&gt;. Another quick manual fix.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/426e0389e77554a288b4649b0e7b63b5/a90e7/angular-17-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVFMIP/xAAWEAEBAQAAAAAAAAAAAAAAAAABEDH/2gAIAQEAAQUCpjP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAADAAMAAAAAAAAAAAAAAAAAASEQEWH/2gAIAQEAAT8hqReDG0GmP//aAAwDAQACAAMAAAAQo8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEAAwEBAAAAAAAAAAAAAAABABEhMbH/2gAIAQEAAT8QsGnO1GzfmadqMlMQSM7P/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate to standalone bootstrap&quot;
        title=&quot;Migrate to standalone bootstrap&quot;
        src=&quot;/static/426e0389e77554a288b4649b0e7b63b5/6a068/angular-17-04.jpg&quot;
        srcset=&quot;/static/426e0389e77554a288b4649b0e7b63b5/09b79/angular-17-04.jpg 240w,
/static/426e0389e77554a288b4649b0e7b63b5/7cc5e/angular-17-04.jpg 480w,
/static/426e0389e77554a288b4649b0e7b63b5/6a068/angular-17-04.jpg 960w,
/static/426e0389e77554a288b4649b0e7b63b5/644c5/angular-17-04.jpg 1440w,
/static/426e0389e77554a288b4649b0e7b63b5/0f98f/angular-17-04.jpg 1920w,
/static/426e0389e77554a288b4649b0e7b63b5/a90e7/angular-17-04.jpg 2022w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;34-migrate-control-flow-syntax&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#34-migrate-control-flow-syntax&quot; aria-label=&quot;34 migrate control flow syntax permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.4 Migrate control flow syntax&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng generate @angular/core:control-flow&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This converted all &lt;code class=&quot;language-text&quot;&gt;*ngIf&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;*ngFor&lt;/code&gt; directives to the new &lt;code class=&quot;language-text&quot;&gt;@if&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@for&lt;/code&gt; syntax across the template files. For example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Before --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;isLoading; else content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Loading...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- After --&gt;&lt;/span&gt;
@if (isLoading) {
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Loading...&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
} @else {
  ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;@for&lt;/code&gt; syntax requires a &lt;code class=&quot;language-text&quot;&gt;track&lt;/code&gt; expression, which the schematic handled automatically. No &lt;code class=&quot;language-text&quot;&gt;track $index&lt;/code&gt; fallbacks needed.&lt;/p&gt;
&lt;p&gt;One thing the new control flow exposed: stricter type checking. Two interface classes (&lt;code class=&quot;language-text&quot;&gt;IssuePriorityIcon&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;IssueTypeWithIcon&lt;/code&gt;) had their &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; field typed as &lt;code class=&quot;language-text&quot;&gt;string&lt;/code&gt; when the methods consuming them expected enum types (&lt;code class=&quot;language-text&quot;&gt;IssuePriority&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;IssueType&lt;/code&gt;). The old &lt;code class=&quot;language-text&quot;&gt;*ngFor&lt;/code&gt; was lenient about this. The new &lt;code class=&quot;language-text&quot;&gt;@for&lt;/code&gt; was not. Fixed by updating the type definitions to use the correct enum types.&lt;/p&gt;
&lt;h2 id=&quot;4-eslint-cleanup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-eslint-cleanup&quot; aria-label=&quot;4 eslint cleanup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. ESLint cleanup&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;@angular-eslint&lt;/code&gt; v17 removed the &lt;code class=&quot;language-text&quot;&gt;ng-cli-compat&lt;/code&gt; config that the project was using. Replaced it with &lt;code class=&quot;language-text&quot;&gt;@angular-eslint/recommended&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@typescript-eslint/recommended&lt;/code&gt;. Also had to upgrade &lt;code class=&quot;language-text&quot;&gt;@typescript-eslint/*&lt;/code&gt; from 5.11.0 to 6.21.0 because the old version did not support TypeScript 5.4.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/259c786f3514bfa7c4fef4b38352e454/f5de0/angular-17-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHlYqIiv//EABYQAAMAAAAAAAAAAAAAAAAAABARIP/aAAgBAQABBQKGP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAQEBAAMAAAAAAAAAAAAAAAEAIRARMf/aAAgBAQABPyFssu0MlPvH/9oADAMBAAIAAwAAABBsz//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAEDAQE/EIf/xAAVEQEBAAAAAAAAAAAAAAAAAAAQIf/aAAgBAgEBPxCn/8QAHBAAAgICAwAAAAAAAAAAAAAAAREAMSFhEEGh/9oACAEBAAE/EGRojS4HbyA2yZzLE+qgxP/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ESLint cleanup&quot;
        title=&quot;ESLint cleanup&quot;
        src=&quot;/static/259c786f3514bfa7c4fef4b38352e454/6a068/angular-17-05.jpg&quot;
        srcset=&quot;/static/259c786f3514bfa7c4fef4b38352e454/09b79/angular-17-05.jpg 240w,
/static/259c786f3514bfa7c4fef4b38352e454/7cc5e/angular-17-05.jpg 480w,
/static/259c786f3514bfa7c4fef4b38352e454/6a068/angular-17-05.jpg 960w,
/static/259c786f3514bfa7c4fef4b38352e454/644c5/angular-17-05.jpg 1440w,
/static/259c786f3514bfa7c4fef4b38352e454/0f98f/angular-17-05.jpg 1920w,
/static/259c786f3514bfa7c4fef4b38352e454/f5de0/angular-17-05.jpg 2018w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;5-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-result&quot; aria-label=&quot;5 result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Result&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/65162ddedf582490e27706311eed96d9/d9e34/angular-17-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAASABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHy+d4Q0dJCAf/EABYQAAMAAAAAAAAAAAAAAAAAAAEQIP/aAAgBAQABBQKyv//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABQQAQAAAAAAAAAAAAAAAAAAADD/2gAIAQEABj8CH//EABoQAAICAwAAAAAAAAAAAAAAAAERABAgIYH/2gAIAQEAAT8hQUOq7l//2gAMAwEAAgADAAAAEOPIfP/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8QH//EAB0QAQACAgIDAAAAAAAAAAAAAAEAERAhMUFRkaH/2gAIAQEAAT8QsLW67YLcfcK0BpXcbdWMTfD6lHiAAx//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 17 migration result&quot;
        title=&quot;Angular 17 migration result&quot;
        src=&quot;/static/65162ddedf582490e27706311eed96d9/6a068/angular-17-06.jpg&quot;
        srcset=&quot;/static/65162ddedf582490e27706311eed96d9/09b79/angular-17-06.jpg 240w,
/static/65162ddedf582490e27706311eed96d9/7cc5e/angular-17-06.jpg 480w,
/static/65162ddedf582490e27706311eed96d9/6a068/angular-17-06.jpg 960w,
/static/65162ddedf582490e27706311eed96d9/644c5/angular-17-06.jpg 1440w,
/static/65162ddedf582490e27706311eed96d9/0f98f/angular-17-06.jpg 1920w,
/static/65162ddedf582490e27706311eed96d9/d9e34/angular-17-06.jpg 2364w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Application compiles and serves without errors.&lt;/p&gt;
&lt;h2 id=&quot;6-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-source-code&quot; aria-label=&quot;6 source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/110&quot;&gt;https://github.com/trungvose/jira-clone-angular/pull/110&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;7-what-i-learned&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-what-i-learned&quot; aria-label=&quot;7 what i learned permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. What I learned&lt;/h2&gt;
&lt;p&gt;The Angular CLI schematics for standalone migration and control flow are surprisingly good. They handled 90% of the work automatically. The remaining 10% were edge cases: a missing import here, a stricter type check there. Nothing that took more than a minute to fix.&lt;/p&gt;
&lt;p&gt;The two-phase approach worked well. Phase 1 (dependency upgrades) followed the exact same pattern from the previous three parts. Phase 2 (code migration) was new territory, but the schematics did the heavy lifting.&lt;/p&gt;
&lt;p&gt;What surprised me was how much code changed. If I had done this manually, it would have been a full day of tedious find-and-replace work. The schematics turned it into a few commands.&lt;/p&gt;
&lt;p&gt;One thing worth noting: the Storybook migration (v6 to v7) was intentionally skipped. Storybook 6.x still works with Angular 17, just with some peer dependency warnings. The v6 to v7 upgrade is a separate, non-trivial migration that deserves its own PR.&lt;/p&gt;
&lt;h2 id=&quot;8-whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#8-whats-next&quot; aria-label=&quot;8 whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. What’s next&lt;/h2&gt;
&lt;p&gt;We are at Angular 17. The remaining path is &lt;code class=&quot;language-text&quot;&gt;17 -&gt; 18 -&gt; 19 -&gt; 20&lt;/code&gt;. Angular 18 should be relatively smooth since the major architectural changes (standalone, control flow) are already done. The esbuild migration is something we will need to tackle eventually, but that can wait.&lt;/p&gt;
&lt;p&gt;See you in Part 5.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrade to Angular 20 from Angular 13 - Part 3: Angular 16 with Claude Code]]></title><link>https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part3-angular-16/</link><guid isPermaLink="false">https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part3-angular-16/</guid><pubDate>Sun, 08 Mar 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The task of upgrading &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; to Angular 20 has been sitting on my backlog forever. If you have been following this series, you know the drill. In &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/&quot;&gt;Part 1&lt;/a&gt; we went from Angular 13 to 14, and in &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part2-angular-15/&quot;&gt;Part 2&lt;/a&gt; we tackled Angular 14 to 15. Each time, the process was mostly the same: run &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt;, hit dependency conflicts, resolve them one by one, test, and commit.&lt;/p&gt;
&lt;p&gt;It is usually tedious to do those migrations manually. But this time, I tried something different. I used &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/overview&quot;&gt;Claude Code&lt;/a&gt; to do the whole thing.&lt;/p&gt;
&lt;h2 id=&quot;1-idea&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-idea&quot; aria-label=&quot;1 idea permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Idea&lt;/h2&gt;
&lt;p&gt;I had two detailed blog posts from the previous migrations (&lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/&quot;&gt;Part 1&lt;/a&gt; and &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part2-angular-15/&quot;&gt;Part 2&lt;/a&gt;) that documented every step, every error, and every resolution. That is a lot of context. So I thought, what if I just ask Claude to read those posts, understand the pattern, and generate a plan for the Angular 16 upgrade?&lt;/p&gt;
&lt;p&gt;I used the &lt;a href=&quot;https://github.com/obra/superpowers&quot;&gt;superpower&lt;/a&gt; workflow to generate a detailed implementation plan.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2070a90d6b826648f4a41733e673a143/0376e/angular-16-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVLkIP/xAAXEAEAAwAAAAAAAAAAAAAAAAARACAh/9oACAEBAAEFAjCFP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAQEBAAAAAAAAAAAAAAAAAABBIP/aAAgBAQAGPwJEx//EAB0QAAECBwAAAAAAAAAAAAAAABEAkQEQITFBUfH/2gAIAQEAAT8hPKDoukW3I0tP/9oADAMBAAIAAwAAABDTz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB0QAAIBBAMAAAAAAAAAAAAAAAABESExUcFhkeH/2gAIAQEAAT8QoHVvMbHP0Dm0B4FicaG3Ph//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;superpower kicked in&quot;
        title=&quot;superpower kicked in&quot;
        src=&quot;/static/2070a90d6b826648f4a41733e673a143/6a068/angular-16-01.jpg&quot;
        srcset=&quot;/static/2070a90d6b826648f4a41733e673a143/09b79/angular-16-01.jpg 240w,
/static/2070a90d6b826648f4a41733e673a143/7cc5e/angular-16-01.jpg 480w,
/static/2070a90d6b826648f4a41733e673a143/6a068/angular-16-01.jpg 960w,
/static/2070a90d6b826648f4a41733e673a143/644c5/angular-16-01.jpg 1440w,
/static/2070a90d6b826648f4a41733e673a143/0f98f/angular-16-01.jpg 1920w,
/static/2070a90d6b826648f4a41733e673a143/0376e/angular-16-01.jpg 2370w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Claude read through both previous posts, understood the proven pattern (upgrade Angular core first with &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt;, then resolve each dependency conflict one at a time), and produced a comprehensive plan.&lt;/p&gt;
&lt;p&gt;After just a few minutes, the plan was ready. I read through all of it. Looked good.&lt;/p&gt;
&lt;h2 id=&quot;2-plan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-plan&quot; aria-label=&quot;2 plan permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Plan&lt;/h2&gt;
&lt;p&gt;Here is a summary of the steps Claude generated:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a new branch&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;trung/v20-migration-part-3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run ng update&lt;/strong&gt; for Angular 16 with &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; (same ESLint chicken-and-egg problem as before)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade TypeScript&lt;/strong&gt; from &lt;code class=&quot;language-text&quot;&gt;~4.8.4&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;~5.0.4&lt;/code&gt; (Angular 16 requires TS 5.0+)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade zone.js&lt;/strong&gt; from &lt;code class=&quot;language-text&quot;&gt;~0.11.4&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;~0.13.0&lt;/code&gt; (significant jump)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade @angular-builders/custom-webpack&lt;/strong&gt; to &lt;code class=&quot;language-text&quot;&gt;16.0.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade @angular-eslint/&lt;/strong&gt;* to &lt;code class=&quot;language-text&quot;&gt;16.3.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade @angular/cdk&lt;/strong&gt; to &lt;code class=&quot;language-text&quot;&gt;16.2.14&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade Ant Design packages&lt;/strong&gt; (&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;16.2.2&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@ant-design/icons-angular&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;16.0.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade ngx-quill&lt;/strong&gt; from &lt;code class=&quot;language-text&quot;&gt;16.1.2&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;22.1.1&lt;/code&gt; (this was the biggest risk, major version jump)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upgrade @ngneat/content-loader&lt;/strong&gt; to &lt;code class=&quot;language-text&quot;&gt;7.0.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify clean npm install&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify ng serve&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify ng build&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Push and create PR&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Claude also identified the key changes in Angular 16 (Signals in developer preview, TypeScript 5.0 support, zone.js bump) and listed potential gotchas, including the ngx-quill major version jump from 16 to 22 since ngx-quill uses its own versioning that does not follow Angular’s.&lt;/p&gt;
&lt;p&gt;The full plan is available in the &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/trung/v20-migration-part-3/docs/plans/2026-03-08-angular-16-upgrade.md&quot;&gt;repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/46a5a8a5ba54421d401f74a2c64242b1/0a29c/angular-16-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 107.91666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAWABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHypYZQdMhAf//EABQQAQAAAAAAAAAAAAAAAAAAADD/2gAIAQEAAQUCH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABQQAQAAAAAAAAAAAAAAAAAAADD/2gAIAQEABj8CH//EABkQAAIDAQAAAAAAAAAAAAAAAAEQABFRcf/aAAgBAQABPyE1jMKPZa//2gAMAwEAAgADAAAAELDIQP/EABURAQEAAAAAAAAAAAAAAAAAABAB/9oACAEDAQE/ECn/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAeEAACAgEFAQAAAAAAAAAAAAABEQAhMRBBUXGR0f/aAAgBAQABPxDcb2Gg66cJZoKBQxMdL23ghIFfIS+Z/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;superpower plan&quot;
        title=&quot;superpower plan&quot;
        src=&quot;/static/46a5a8a5ba54421d401f74a2c64242b1/6a068/angular-16-02.jpg&quot;
        srcset=&quot;/static/46a5a8a5ba54421d401f74a2c64242b1/09b79/angular-16-02.jpg 240w,
/static/46a5a8a5ba54421d401f74a2c64242b1/7cc5e/angular-16-02.jpg 480w,
/static/46a5a8a5ba54421d401f74a2c64242b1/6a068/angular-16-02.jpg 960w,
/static/46a5a8a5ba54421d401f74a2c64242b1/644c5/angular-16-02.jpg 1440w,
/static/46a5a8a5ba54421d401f74a2c64242b1/0f98f/angular-16-02.jpg 1920w,
/static/46a5a8a5ba54421d401f74a2c64242b1/0a29c/angular-16-02.jpg 2329w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;3-kicking-off-the-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-kicking-off-the-process&quot; aria-label=&quot;3 kicking off the process permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Kicking off the process&lt;/h2&gt;
&lt;p&gt;Once I reviewed the plan, I kicked off the execution. Claude worked through each task sequentially, following the exact same pattern we established in Parts 1 and 2:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the version in &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code class=&quot;language-text&quot;&gt;npm install --force&lt;/code&gt; (since not all dependencies are resolved yet)&lt;/li&gt;
&lt;li&gt;Verify with &lt;code class=&quot;language-text&quot;&gt;npm view &amp;lt;package&gt;@&amp;lt;version&gt; peerDependencies --json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Commit the changes&lt;/li&gt;
&lt;li&gt;Move to the next dependency&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The whole process ran for about 20 minutes. I mostly just watched and approved the commands. There was one moment where Claude needed to resolve an unexpected issue with ngx-quill’s API changes, but it handled that by searching the codebase for quill usage and checking compatibility.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b0e1549db83182d9f146a7c49a1e7cb/48f41/angular-16-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 102.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVlSQ0XAYD/xAAYEAACAwAAAAAAAAAAAAAAAAAAARARIP/aAAgBAQABBQIcUPP/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAY/Ah//xAAYEAADAQEAAAAAAAAAAAAAAAAAARAxcf/aAAgBAQABPyF3jUdKMc//2gAMAwEAAgADAAAAECMIPP/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8QH//EAB0QAQACAgIDAAAAAAAAAAAAAAEAESExEFFBYZH/2gAIAQEAAT8QAVuvRPDhFFLHcC7wdMpmBo+Tbj//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude one shot implementation&quot;
        title=&quot;Claude one shot implementation&quot;
        src=&quot;/static/4b0e1549db83182d9f146a7c49a1e7cb/6a068/angular-16-03.jpg&quot;
        srcset=&quot;/static/4b0e1549db83182d9f146a7c49a1e7cb/09b79/angular-16-03.jpg 240w,
/static/4b0e1549db83182d9f146a7c49a1e7cb/7cc5e/angular-16-03.jpg 480w,
/static/4b0e1549db83182d9f146a7c49a1e7cb/6a068/angular-16-03.jpg 960w,
/static/4b0e1549db83182d9f146a7c49a1e7cb/644c5/angular-16-03.jpg 1440w,
/static/4b0e1549db83182d9f146a7c49a1e7cb/0f98f/angular-16-03.jpg 1920w,
/static/4b0e1549db83182d9f146a7c49a1e7cb/48f41/angular-16-03.jpg 1997w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-result&quot; aria-label=&quot;4 result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Result&lt;/h2&gt;
&lt;p&gt;After all 14 tasks completed, I ran &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; and things worked as expected. The application compiled and served without errors. Board view, issue detail, drag and drop, everything functioned correctly.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b807615e9ba79470bbc19c7e9b15eabc/3195e/angular-16-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAByqsVAQV//8QAFRABAQAAAAAAAAAAAAAAAAAAICH/2gAIAQEAAQUCq//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAIDAAAAAAAAAAAAAAAAAAARASBR/9oACAEBAAE/IZY0dP/aAAwDAQACAAMAAAAQQM//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCH/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/EB2f/8QAGhABAAIDAQAAAAAAAAAAAAAAAQARECExcf/aAAgBAQABPxBIFaiI2PNRT2XyLtx//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Claude one shot implementation&quot;
        title=&quot;Claude one shot implementation&quot;
        src=&quot;/static/b807615e9ba79470bbc19c7e9b15eabc/6a068/angular-16-04.jpg&quot;
        srcset=&quot;/static/b807615e9ba79470bbc19c7e9b15eabc/09b79/angular-16-04.jpg 240w,
/static/b807615e9ba79470bbc19c7e9b15eabc/7cc5e/angular-16-04.jpg 480w,
/static/b807615e9ba79470bbc19c7e9b15eabc/6a068/angular-16-04.jpg 960w,
/static/b807615e9ba79470bbc19c7e9b15eabc/644c5/angular-16-04.jpg 1440w,
/static/b807615e9ba79470bbc19c7e9b15eabc/0f98f/angular-16-04.jpg 1920w,
/static/b807615e9ba79470bbc19c7e9b15eabc/3195e/angular-16-04.jpg 3089w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here is the dependency version map for reference:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Before (v15)&lt;/th&gt;
&lt;th&gt;After (v16)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^15.2.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.12&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^15.2.11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.14&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;typescript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~4.8.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~5.0.4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;zone.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~0.11.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;~0.13.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^15.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.1.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^22.1.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^15.2.9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;^16.2.14&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;No code changes were needed beyond the dependency upgrades. Angular 16 introduces Signals in developer preview, but there is no migration needed yet. Standalone components and control flow syntax migration are deferred to the Angular 17 step.&lt;/p&gt;
&lt;h2 id=&quot;5-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-source-code&quot; aria-label=&quot;5 source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/109&quot;&gt;https://github.com/trungvose/jira-clone-angular/pull/109&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;6-what-i-learned&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-what-i-learned&quot; aria-label=&quot;6 what i learned permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. What I learned&lt;/h2&gt;
&lt;p&gt;Doing the first two migrations manually and documenting them thoroughly turned out to be the best investment. Those blog posts became the training data for Claude to understand the exact pattern and replicate it. The third migration took about 30 minutes total: 10 minutes of prompting and reviewing the plan, then a single execution run by Claude. What used to be an hour of hands-on work became mostly watching and approving.&lt;/p&gt;
&lt;p&gt;The key is that the pattern was well-established:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; for the initial &lt;code class=&quot;language-text&quot;&gt;ng update&lt;/code&gt; to bypass the ESLint peer dependency conflict&lt;/li&gt;
&lt;li&gt;Resolve each dependency conflict one at a time using &lt;code class=&quot;language-text&quot;&gt;npm view&lt;/code&gt; to check versions and peer dependencies&lt;/li&gt;
&lt;li&gt;Commit after each successful step so you can always roll back&lt;/li&gt;
&lt;li&gt;Test with both &lt;code class=&quot;language-text&quot;&gt;ng serve&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;ng build&lt;/code&gt; since production builds are stricter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have repetitive tasks with a clear pattern, documenting the first few iterations thoroughly and then handing it off to AI is a solid approach.&lt;/p&gt;
&lt;h2 id=&quot;7-whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-whats-next&quot; aria-label=&quot;7 whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. What’s next&lt;/h2&gt;
&lt;p&gt;We are at Angular 16 now. The remaining path is &lt;code class=&quot;language-text&quot;&gt;16 -&gt; 17 -&gt; 18 -&gt; 19 -&gt; 20&lt;/code&gt;. Angular 17 is going to be the interesting one since that is where standalone components and the new control flow syntax (&lt;code class=&quot;language-text&quot;&gt;@if&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@for&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@switch&lt;/code&gt;) become the default. That will require actual code changes, not just dependency bumps.&lt;/p&gt;
&lt;p&gt;See you in Part 4.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[I added synced lyrics to Angular Spotify without writing a single line of code]]></title><description><![CDATA[How I used Claude Code to implement a lyrics feature for Angular Spotify, from design to working code, step by step.]]></description><link>https://trungvose.comangular-spotify-lyrics-agentic-coding/</link><guid isPermaLink="false">https://trungvose.comangular-spotify-lyrics-agentic-coding/</guid><pubDate>Sun, 01 Mar 2026 02:30:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been lately catching up with AI tooling. When exploring AI tooling for work at Ascenda, using &lt;a href=&quot;https://help.figma.com/hc/en-us/articles/32132100833559-Guide-to-the-Figma-MCP-server&quot;&gt;Figma MCP&lt;/a&gt; to go from design to code was impressive and really opened my eyes on what’s possible. So today, I wanted to try something bigger: implement a feature that was requested &lt;strong&gt;years ago&lt;/strong&gt; for &lt;a href=&quot;https://github.com/trungvose/angular-spotify&quot;&gt;Angular Spotify&lt;/a&gt;, synced lyrics display.&lt;/p&gt;
&lt;p&gt;With AI-assisted coding, it is becoming so easy to implement something that would have taken me days of research and wiring up. I did the whole thing purely by &lt;strong&gt;prompting&lt;/strong&gt; with &lt;a href=&quot;https://claude.ai/claude-code&quot;&gt;Claude Code&lt;/a&gt;, and I want to share the step-by-step process.&lt;/p&gt;
&lt;h2 id=&quot;the-feature&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-feature&quot; aria-label=&quot;the feature permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The feature&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/db0cabf3e0f7a7cf1dfbbb2da763ed9a/00-demo.gif&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Synced lyrics that scroll along with the music, just like you see in the official Spotify app. Click a line to jump to that part of the song. When synced lyrics aren’t available, it falls back to plain lyrics with manual scroll.&lt;/p&gt;
&lt;p&gt;I didn’t write any code myself at first. There were two prompts that I sent to Claude:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/init&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A prompt starting with &lt;code class=&quot;language-text&quot;&gt;Help me to plan this feature&lt;/code&gt; to trigger the &lt;a href=&quot;https://github.com/obra/superpowers&quot;&gt;Superpower&lt;/a&gt; workflow that goes through brainstorming, writing plans, and subagent-driven development&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h3&gt;
&lt;p&gt;Here is my PR for reference:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/angular-spotify&quot;&gt;https://github.com/trungvose/angular-spotify&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-1-init-command&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-init-command&quot; aria-label=&quot;step 1 init command permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: init command&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;/init&lt;/code&gt; creates a &lt;code class=&quot;language-text&quot;&gt;CLAUDE.md&lt;/code&gt; file with a high-level summary of the project. I learned that the output by Claude contains enough good information about the project to get started.&lt;/p&gt;
&lt;p&gt;However, you should also include one of the most important sections: &lt;strong&gt;how to build and verify&lt;/strong&gt;. After each change, Claude can run those steps to verify things are working.&lt;/p&gt;
&lt;p&gt;For example, Claude will by default figure out it needs to run lint/test. But maybe what is more important in your particular project is to run &lt;code class=&quot;language-text&quot;&gt;yarn start&lt;/code&gt; and see if there are any errors at runtime.&lt;/p&gt;
&lt;p&gt;This is crucial for the agent to keep running and correcting itself without needing you in the loop as much.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/569af5e185ebb25387d11d087451ddd3/ab590/00-init.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 101.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHyJrJAdIEB/8QAFBABAAAAAAAAAAAAAAAAAAAAMP/aAAgBAQABBQIf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAFBABAAAAAAAAAAAAAAAAAAAAMP/aAAgBAQAGPwIf/8QAGRAAAgMBAAAAAAAAAAAAAAAAABARIUFR/9oACAEBAAE/IceFdU0Sv//aAAwDAQACAAMAAAAQkMA8/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPxAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPxAf/8QAHRABAAICAgMAAAAAAAAAAAAAAQARMUEQYbHw8f/aAAgBAQABPxBGgVZuK78cKItpruPr8jWoqmJaJeVn/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Init command generating CLAUDE.md&quot;
        title=&quot;Init command generating CLAUDE.md&quot;
        src=&quot;/static/569af5e185ebb25387d11d087451ddd3/6a068/00-init.jpg&quot;
        srcset=&quot;/static/569af5e185ebb25387d11d087451ddd3/09b79/00-init.jpg 240w,
/static/569af5e185ebb25387d11d087451ddd3/7cc5e/00-init.jpg 480w,
/static/569af5e185ebb25387d11d087451ddd3/6a068/00-init.jpg 960w,
/static/569af5e185ebb25387d11d087451ddd3/644c5/00-init.jpg 1440w,
/static/569af5e185ebb25387d11d087451ddd3/0f98f/00-init.jpg 1920w,
/static/569af5e185ebb25387d11d087451ddd3/ab590/00-init.jpg 2337w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-2-superpower-workflow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-superpower-workflow&quot; aria-label=&quot;step 2 superpower workflow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: Superpower workflow&lt;/h2&gt;
&lt;h3 id=&quot;21-brainstorm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#21-brainstorm&quot; aria-label=&quot;21 brainstorm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.1 Brainstorm&lt;/h3&gt;
&lt;p&gt;I sent a prompt to trigger the &lt;a href=&quot;https://github.com/obra/superpowers&quot;&gt;Superpower&lt;/a&gt; workflow that includes several different skills to help me go through brainstorming, design, and implementation:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0f7fd11314f101c418d8957a38ef66a9/b3174/01-brainstorm.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAQAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByRAqP//EABYQAQEBAAAAAAAAAAAAAAAAABARAP/aAAgBAQABBQJup//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAMAAwAAAAAAAAAAAAAAAAABERBBcf/aAAgBAQABPyHRcOjhFP/aAAwDAQACAAMAAAAQox//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxBX/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHRAAAwACAgMAAAAAAAAAAAAAAAERIVFBYXGhwf/aAAgBAQABPxDPH0R0YY1FUm5oi/IdPXhH/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Brainstorm steps&quot;
        title=&quot;Brainstorm steps&quot;
        src=&quot;/static/0f7fd11314f101c418d8957a38ef66a9/6a068/01-brainstorm.jpg&quot;
        srcset=&quot;/static/0f7fd11314f101c418d8957a38ef66a9/09b79/01-brainstorm.jpg 240w,
/static/0f7fd11314f101c418d8957a38ef66a9/7cc5e/01-brainstorm.jpg 480w,
/static/0f7fd11314f101c418d8957a38ef66a9/6a068/01-brainstorm.jpg 960w,
/static/0f7fd11314f101c418d8957a38ef66a9/644c5/01-brainstorm.jpg 1440w,
/static/0f7fd11314f101c418d8957a38ef66a9/0f98f/01-brainstorm.jpg 1920w,
/static/0f7fd11314f101c418d8957a38ef66a9/b3174/01-brainstorm.jpg 2377w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Help me to plan this feature.&lt;/p&gt;
&lt;p&gt;I want to connect to Musixmatch, fetch the lyrics and show it on the screen. As you rewind the song, the lyrics will move to the right place.&lt;/p&gt;
&lt;p&gt;Also, if you click into a lyric, it will jump to the right position of the song.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first line &lt;code class=&quot;language-text&quot;&gt;Help me to plan this feature&lt;/code&gt; automatically triggers the brainstorm workflow. Claude does research first, then asks me a couple of follow-up questions. The questions it asked were pretty accurate, see below.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f735d662a6bcbc316e12b9942b5debbb/2527c/02-brainstorm.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcyHAyQ//8QAFhABAQEAAAAAAAAAAAAAAAAAEQAQ/9oACAEBAAEFAmZxz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAMBAQAAAAAAAAAAAAAAAAAQETFB/9oACAEBAAE/IcFpylL/2gAMAwEAAgADAAAAEHff/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEB/9oACAEDAQE/EKuv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQARIVExQbH/2gAIAQEAAT8Qu4eR0kWuScm3Wo2YZbuf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Brainstorm steps&quot;
        title=&quot;Brainstorm steps&quot;
        src=&quot;/static/f735d662a6bcbc316e12b9942b5debbb/6a068/02-brainstorm.jpg&quot;
        srcset=&quot;/static/f735d662a6bcbc316e12b9942b5debbb/09b79/02-brainstorm.jpg 240w,
/static/f735d662a6bcbc316e12b9942b5debbb/7cc5e/02-brainstorm.jpg 480w,
/static/f735d662a6bcbc316e12b9942b5debbb/6a068/02-brainstorm.jpg 960w,
/static/f735d662a6bcbc316e12b9942b5debbb/644c5/02-brainstorm.jpg 1440w,
/static/f735d662a6bcbc316e12b9942b5debbb/0f98f/02-brainstorm.jpg 1920w,
/static/f735d662a6bcbc316e12b9942b5debbb/2527c/02-brainstorm.jpg 2345w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;22-design&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#22-design&quot; aria-label=&quot;22 design permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.2 Design&lt;/h3&gt;
&lt;p&gt;From the screenshot below, you can see there are a few steps in the brainstorming phase:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Explore project context for the lyrics feature&lt;/li&gt;
&lt;li&gt;Ask clarifying questions&lt;/li&gt;
&lt;li&gt;Propose approaches with trade-offs&lt;/li&gt;
&lt;li&gt;Present design and get approval&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At step 3, based on all my clarifications, Claude suggested a couple of approaches and I could choose which one I wanted. I chose option A which was also the recommended option.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d337626bd1fe72386e6198772adb957e/2121a/03-design.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVnWACwID//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAEFAh//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAY/Ah//xAAZEAACAwEAAAAAAAAAAAAAAAAAARARIYH/2gAIAQEAAT8hHDWHCxvEXH//2gAMAwEAAgADAAAAEFAIPP/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8QH//EAB0QAQACAgIDAAAAAAAAAAAAAAEAESExUZFBgaH/2gAIAQEAAT8QDBg1EXruLnx1EANXWotbR7iOPsQA4ipc/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Brainstorm Approach&quot;
        title=&quot;Brainstorm Approach&quot;
        src=&quot;/static/d337626bd1fe72386e6198772adb957e/6a068/03-design.jpg&quot;
        srcset=&quot;/static/d337626bd1fe72386e6198772adb957e/09b79/03-design.jpg 240w,
/static/d337626bd1fe72386e6198772adb957e/7cc5e/03-design.jpg 480w,
/static/d337626bd1fe72386e6198772adb957e/6a068/03-design.jpg 960w,
/static/d337626bd1fe72386e6198772adb957e/644c5/03-design.jpg 1440w,
/static/d337626bd1fe72386e6198772adb957e/0f98f/03-design.jpg 1920w,
/static/d337626bd1fe72386e6198772adb957e/2121a/03-design.jpg 2371w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Next, it produced a detailed design document in markdown covering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Musixmatch API with plain lyrics (just text lines) and synced/timestamped lyrics&lt;/li&gt;
&lt;li&gt;Library structure following the existing Nx conventions&lt;/li&gt;
&lt;li&gt;Data models and store design&lt;/li&gt;
&lt;li&gt;UI component breakdown&lt;/li&gt;
&lt;li&gt;The full data flow from playback state to active lyric line&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This design phase was crucial. Instead of jumping straight into code, I got a clear blueprint that I could review and adjust before writing a single line.&lt;/p&gt;
&lt;p&gt;If I approve the design, it goes ahead and creates a very detailed implementation plan, also in markdown.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/angular-spotify/blob/fdceb66660860d81385c808dbc021d267b304563/docs/plans/2026-02-28-lyrics-feature-design.md&quot;&gt;Here is the design doc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/106/changes/090889718bbd50dc6d46a95c7e9889b75ff97b19&quot;&gt;Here is the commit&lt;/a&gt; where you can see the implementation plan is really long (+1k lines), covering how to generate new libraries, how to structure code, etc. But eventually after a lot of follow-up prompts during implementation, the plan got outdated very quickly. So I deleted it from the final PR.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-3-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-implementation&quot; aria-label=&quot;step 3 implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3: Implementation&lt;/h2&gt;
&lt;p&gt;After having the implementation plan, I let Claude run and generate all of the code. As you can see from the following &lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/106/changes/262161812d2f7fdca90e8919394635d7fe8c2e55&quot;&gt;commit&lt;/a&gt;, the Musixmatch service was being implemented and I just needed to provide an API key at the end to test the app.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0ce463d7b2374e9698a76d1491e6c215/a99bc/04-musixmatch.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABYBAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAAB5GxbMiP/xAAYEAACAwAAAAAAAAAAAAAAAAABEAIhMf/aAAgBAQABBQIWVHV//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAHBAAAgEFAQAAAAAAAAAAAAAAAAEREDFBUXGB/9oACAEBAAE/IYZPRpaIRe4Oaf/aAAwDAQACAAMAAAAQNO//xAAWEQEBAQAAAAAAAAAAAAAAAAABECH/2gAIAQMBAT8QXZ//xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQIBAT8QW1v/xAAcEAEAAgEFAAAAAAAAAAAAAAABACEQETFhgaH/2gAIAQEAAT8QTN1LBae04J6EIUXXH//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Musixmatch&quot;
        title=&quot;Musixmatch&quot;
        src=&quot;/static/0ce463d7b2374e9698a76d1491e6c215/6a068/04-musixmatch.jpg&quot;
        srcset=&quot;/static/0ce463d7b2374e9698a76d1491e6c215/09b79/04-musixmatch.jpg 240w,
/static/0ce463d7b2374e9698a76d1491e6c215/7cc5e/04-musixmatch.jpg 480w,
/static/0ce463d7b2374e9698a76d1491e6c215/6a068/04-musixmatch.jpg 960w,
/static/0ce463d7b2374e9698a76d1491e6c215/644c5/04-musixmatch.jpg 1440w,
/static/0ce463d7b2374e9698a76d1491e6c215/0f98f/04-musixmatch.jpg 1920w,
/static/0ce463d7b2374e9698a76d1491e6c215/a99bc/04-musixmatch.jpg 2881w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, when I went to the website to log in and get the API key, I realised they had deleted my account. The whole developer portal seemed to have changed since I created an account years ago.&lt;/p&gt;
&lt;p&gt;So I asked Claude to do a quick research and find an alternative and it found &lt;a href=&quot;https://lrclib.net&quot;&gt;LRCLIB&lt;/a&gt;, a free, open lyrics API that requires no API key. That ended up being what we used in the final PR.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fea92e91e0007257ad88c1e2f6c66676/caf72/04-implementation.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMCBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeTmkgD/xAAVEAEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAQABBQJh/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAFxABAAMAAAAAAAAAAAAAAAAAEAERMf/aAAgBAQABPyGcaX//2gAMAwEAAgADAAAAEBcP/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAICAgMAAAAAAAAAAAAAAAABESEQUTFBof/aAAgBAQABPxCHRcbHG/Sho5NWOnj/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Implementation with LRCLIB&quot;
        title=&quot;Implementation with LRCLIB&quot;
        src=&quot;/static/fea92e91e0007257ad88c1e2f6c66676/6a068/04-implementation.jpg&quot;
        srcset=&quot;/static/fea92e91e0007257ad88c1e2f6c66676/09b79/04-implementation.jpg 240w,
/static/fea92e91e0007257ad88c1e2f6c66676/7cc5e/04-implementation.jpg 480w,
/static/fea92e91e0007257ad88c1e2f6c66676/6a068/04-implementation.jpg 960w,
/static/fea92e91e0007257ad88c1e2f6c66676/644c5/04-implementation.jpg 1440w,
/static/fea92e91e0007257ad88c1e2f6c66676/0f98f/04-implementation.jpg 1920w,
/static/fea92e91e0007257ad88c1e2f6c66676/caf72/04-implementation.jpg 3108w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-4-follow-up-and-debugging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-4-follow-up-and-debugging&quot; aria-label=&quot;step 4 follow up and debugging permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 4: Follow-up and debugging&lt;/h2&gt;
&lt;p&gt;Once the code was generated, I ran the dev server and manually verified how the feature worked. There is a lot of opportunity here to use something like Chrome MCP so Claude can run and verify the feature by itself. But at this stage, the workflow was mainly: I spot a bug or unexpected behavior, prompt Claude about it, and Claude fixes it. A few worth mentioning:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CORS handling.&lt;/strong&gt; The auth interceptor was attaching the Spotify &lt;code class=&quot;language-text&quot;&gt;Authorization&lt;/code&gt; header to LRCLIB requests, which LRCLIB’s CORS policy rejects. Claude identified both interceptors (auth + unauthorized) that needed to skip &lt;code class=&quot;language-text&quot;&gt;lrclib.net&lt;/code&gt; requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Position drift.&lt;/strong&gt; The &lt;code class=&quot;language-text&quot;&gt;stateTimestamp&lt;/code&gt; needed to be captured in &lt;code class=&quot;language-text&quot;&gt;PlaybackService&lt;/code&gt; at the moment the SDK fires &lt;code class=&quot;language-text&quot;&gt;player_state_changed&lt;/code&gt;, &lt;strong&gt;before&lt;/strong&gt; the async &lt;code class=&quot;language-text&quot;&gt;getVolume()&lt;/code&gt; call. Otherwise there was a 3-4 second delay where the music runs ahead and the lyrics catch up a few seconds later. After a couple of prompts and testing, it was fixed!&lt;/p&gt;
&lt;h3 id=&quot;23-optional---dangerously-skip-permissions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#23-optional---dangerously-skip-permissions&quot; aria-label=&quot;23 optional   dangerously skip permissions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.3 Optional: &lt;code class=&quot;language-text&quot;&gt;--dangerously-skip-permissions&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Claude may ask for many permissions while reading from Figma and your codebase.&lt;/p&gt;
&lt;p&gt;I’ve been running with &lt;code class=&quot;language-text&quot;&gt;--dangerously-skip-permissions&lt;/code&gt;, which means Claude won’t pause to ask before executing certain commands.&lt;/p&gt;
&lt;p&gt;So far, I haven’t hit issues, and it keeps the session moving. Without it, the work is interuppted: you step away for a moment, come back, and discover it stopped after 30 seconds to ask a question.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;However, &lt;code class=&quot;language-text&quot;&gt;--dangerously-skip-permissions&lt;/code&gt; is still a security concern, so we should consider configuring &lt;code class=&quot;language-text&quot;&gt;setting.json&lt;/code&gt; to whitelist commands that we often use.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;what-i-learned&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-i-learned&quot; aria-label=&quot;what i learned permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What I learned&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Design first, code second.&lt;/strong&gt; Having Claude produce a design document before any code was the most valuable step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI-assisted coding shines for pattern-heavy work.&lt;/strong&gt; The lyrics feature mirrors the visualizer’s architecture. Claude recognized the patterns in my codebase and replicated them consistently across all the new files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You still need to review.&lt;/strong&gt; I’m not blindly accepting everything. But the feedback loop is much tighter. Instead of writing code and debugging, I’m reviewing code and prompting for adjustments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests matter more than ever.&lt;/strong&gt; The biggest gap in my Spotify codebase is the lack of unit tests. I still had to manually test every change. If I had a proper unit test suite, or even better e2e tests, Claude could handle a lot of that verification and make the whole process even smoother. That’s something I want to invest in next.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Features that sat in the backlog for years took an afternoon.&lt;/strong&gt; That’s the real takeaway.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Google TypeScript Style Guide]]></title><link>https://trungvose.comgoogle-typescript-style-guide/</link><guid isPermaLink="false">https://trungvose.comgoogle-typescript-style-guide/</guid><pubDate>Sun, 21 Dec 2025 07:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just realised my last blog post was in July 2025. Since then, I have attended Google I/O Connect 2025 Shanghai in August, spoke at &lt;a href=&quot;/talks/2025-11-05-oredev-performance/&quot;&gt;Øredev 2025&lt;/a&gt; in Sweden in November, and then &lt;a href=&quot;/talks/2025-11-16-jsconfjp/&quot;&gt;JSConfJP 2025&lt;/a&gt; just a few days after coming back from Sweden. It has been a busy second half of the year.&lt;/p&gt;
&lt;p&gt;In between all the travel and talks, I finally had some quiet time to sit down and read the &lt;a href=&quot;https://google.github.io/styleguide/tsguide.html&quot;&gt;Google TypeScript Style Guide&lt;/a&gt; and picked up quite a few solid lessons along the way. Some points were already familiar, but others helped clear up long standing questions, for example whether iterating objects with for of and Object.keys is actually the right approach, or when to prefer interfaces over type literal aliases. Below are the tips I found most useful. Sharing them here in case they help you as well.&lt;/p&gt;
&lt;h2 id=&quot;432-iterating-objects&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#432-iterating-objects&quot; aria-label=&quot;432 iterating objects permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.3.2 &lt;strong&gt;Iterating objects&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Iterating objects with &lt;code class=&quot;language-text&quot;&gt;for (... in ...)&lt;/code&gt; is error prone. It will include enumerable properties from the prototype chain.&lt;/p&gt;
&lt;p&gt;Do not use unfiltered &lt;code class=&quot;language-text&quot;&gt;for (... in ...)&lt;/code&gt; statements:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ DO NOT USE&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; someObj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// x could come from some parent prototype!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Either filter values explicitly with an &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; statement, or use &lt;code class=&quot;language-text&quot;&gt;for (... of Object.keys(...))&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ USE&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; someObj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;someObj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// now x was definitely defined on someObj&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someObj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// note: for _of_!&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// now x was definitely defined on someObj&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someObj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// note: for _of_!&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// now key was definitely defined on someObj&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;445-class-members&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#445-class-members&quot; aria-label=&quot;445 class members permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.4.5 Class members&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;no-private-fields&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#no-private-fields&quot; aria-label=&quot;no private fields permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;No #private fields&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Do not&lt;/strong&gt; use private fields (also known as private identifiers):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ DO NOT USE&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Clazz&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  #ident &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead, use TypeScript’s visibility annotations:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ USE&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Clazz&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; ident &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Private identifiers cause substantial emit size and performance regressions when down-leveled by TypeScript, and are unsupported before ES2015. They can only be downleveled to ES2015, not lower. At the same time, they do not offer substantial benefits when static type checking is used to enforce visibility.&lt;/p&gt;
&lt;h2 id=&quot;452-prefer-function-declarations-for-named-functions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#452-prefer-function-declarations-for-named-functions&quot; aria-label=&quot;452 prefer function declarations for named functions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.5.2 Prefer function declarations for named functions&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Prefer function declarations over arrow functions or function expressions when defining named functions.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Prefer this: function declarations&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Over arrow functions&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Arrow functions &lt;em&gt;may&lt;/em&gt; be used, for example, when an explicit type annotation is required.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchFunction&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;source&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; subString&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fooSearch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;SearchFunction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;source&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; subString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;455-arrow-function-bodies&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#455-arrow-function-bodies&quot; aria-label=&quot;455 arrow function bodies permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.5.5 &lt;strong&gt;Arrow function bodies&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Use arrow functions with concise bodies (i.e. expressions) or block bodies as appropriate.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Top level functions use function declarations.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Block bodies are fine:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; receipts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; books&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Book&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; receipt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;payMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;recordTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;receipt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; receipt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Concise bodies are fine, too, if the return value is used:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; longThings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myValues&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;payMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;amount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// function declarations are fine, but must not access `this`.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Nested arrow functions may be assigned to a const.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;computeTax&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;amount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; amount &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Only use a concise body if the return value of the function is actually used. The block body makes sure the return type is &lt;code class=&quot;language-text&quot;&gt;void&lt;/code&gt; then and prevents potential side effects.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD: use a block body if the return value of the function is not used.&lt;/span&gt;
myPromise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD: this typechecks, but the return value still leaks.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function-variable function&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD: return value is unused, use a block body.&lt;/span&gt;
myPromise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD: code may use blocks for readability.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transformed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; intermediate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someComplicatedExpr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; more &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;acrossManyLines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;intermediate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;worthWrapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;more&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD: explicit `void` ensures no leaked return value&lt;/span&gt;
myPromise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tip: The &lt;code class=&quot;language-text&quot;&gt;void&lt;/code&gt; operator can be used to ensure an arrow function with an expression body returns &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; when the result is unused.&lt;/p&gt;
&lt;h2 id=&quot;457-prefer-passing-arrow-functions-as-callbacks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#457-prefer-passing-arrow-functions-as-callbacks&quot; aria-label=&quot;457 prefer passing arrow functions as callbacks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.5.7 Prefer passing arrow functions as callbacks&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Callbacks can be invoked with unexpected arguments that can pass a type check but still result in logical errors.&lt;/p&gt;
&lt;p&gt;Avoid passing a named callback to a higher-order function, unless you are sure of the stability of both functions’ call signatures. Beware, in particular, of less-commonly-used optional parameters.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD: Arguments are not explicitly passed, leading to unintended behavior&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// when the optional `radix` argument gets the array indices 0, 1, and 2.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; numbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;11&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;5&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;10&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parseInt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// &gt; [11, NaN, 2];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead, prefer passing an &lt;code class=&quot;language-text&quot;&gt;arrow-function&lt;/code&gt; that explicitly forwards parameters to the named callback.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD: Arguments are explicitly passed to the callback&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; numbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;11&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;5&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// &gt; [11, 5, 3]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD: Function is locally defined and is designed to be used as a callback&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dayFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; element &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;day&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; days &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tuesday&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;juice&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;wednesday&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dayFilter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;4510-parameter-initializers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4510-parameter-initializers&quot; aria-label=&quot;4510 parameter initializers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.5.10 Parameter initializers&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Optional function parameters &lt;em&gt;may&lt;/em&gt; be given a default initializer to use when the argument is omitted. Initializers &lt;em&gt;must not&lt;/em&gt; have any observable side effects. Initializers &lt;em&gt;should&lt;/em&gt; be kept as simple as possible.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; extraContext&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD: side effect of incrementing the counter&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; globalCounter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; globalCounter&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD: exposes shared mutable state, which can introduce unintended coupling&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// between function calls&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; defaultPaths&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;frobnicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;paths &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; defaultPaths&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use default parameters sparingly. Prefer &lt;a href=&quot;https://google.github.io/styleguide/tsguide.html#features-objects-destructuring&quot;&gt;destructuring&lt;/a&gt; to create readable APIs when there are more than a small handful of optional parameters that do not have a natural order.&lt;/p&gt;
&lt;h2 id=&quot;481-string-literals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#481-string-literals&quot; aria-label=&quot;481 string literals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.8.1 String literals&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;use-single-quotes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#use-single-quotes&quot; aria-label=&quot;use single quotes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Use single quotes&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Ordinary string literals are delimited with single quotes (&lt;code class=&quot;language-text&quot;&gt;&apos;&lt;/code&gt;), rather than double quotes (&lt;code class=&quot;language-text&quot;&gt;&quot;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Tip: if a string contains a single quote character, consider using a template string to avoid having to escape the quote.&lt;/p&gt;
&lt;h3 id=&quot;no-line-continuations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#no-line-continuations&quot; aria-label=&quot;no line continuations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;No line continuations&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Do not use &lt;em&gt;line continuations&lt;/em&gt; (that is, ending a line inside a string literal with a backslash) in either ordinary or template string literals. Even though ES5 allows this, it can lead to tricky errors if any trailing whitespace comes after the slash, and is less obvious to readers.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ Disallowed&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LONG_STRING&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;This is a very very very very very very very long string. \
    It inadvertently contains long stretches of spaces due to how the \
    continued lines are indented.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ Instead, write&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LONG_STRING&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;This is a very very very very very very long string. &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;It does not contain long stretches of spaces because it uses &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;concatenated strings.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SINGLE_STRING&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;http://it.is.also/acceptable_to_use_a_single_long_string_when_breaking_would_hinder_search_discoverability&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;template-literals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#template-literals&quot; aria-label=&quot;template literals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Template literals&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Use template literals (delimited with ```) over complex string concatenation, particularly if multiple string literals are involved. Template literals may span multiple lines.&lt;/p&gt;
&lt;p&gt;If a template literal spans multiple lines, it does not need to follow the indentation of the enclosing block, though it may if the added whitespace does not matter.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;arithmetic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Here is a table of arithmetic operations:
&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; + &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; * &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; / &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; = &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;483-type-coercion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#483-type-coercion&quot; aria-label=&quot;483 type coercion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.8.3 Type coercion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript code &lt;em&gt;may&lt;/em&gt; use the &lt;code class=&quot;language-text&quot;&gt;String()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Boolean()&lt;/code&gt; (note: no &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt;!) functions, string template literals, or &lt;code class=&quot;language-text&quot;&gt;!!&lt;/code&gt; to coerce types.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; str &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aNumber&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bool2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; str2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;result: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;bool2&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Values of enum types (including unions of enum types and other types) &lt;em&gt;must not&lt;/em&gt; be converted to booleans with &lt;code class=&quot;language-text&quot;&gt;Boolean()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;!!&lt;/code&gt;, and must instead be compared explicitly with comparison operators.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; SupportLevel &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;NONE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;BASIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;ADVANCED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SupportLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; enabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; maybeLevel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SupportLevel&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
enabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;maybeLevel&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SupportLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; enabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; level &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; SupportLevel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NONE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; maybeLevel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SupportLevel&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
enabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; level &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; level &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; SupportLevel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NONE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Code &lt;em&gt;must&lt;/em&gt; use &lt;code class=&quot;language-text&quot;&gt;Number()&lt;/code&gt; to parse numeric values, and &lt;em&gt;must&lt;/em&gt; check its return for &lt;code class=&quot;language-text&quot;&gt;NaN&lt;/code&gt; values explicitly, unless failing to parse is impossible from context.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; aNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;123&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFinite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aNumber&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Code also &lt;em&gt;must not&lt;/em&gt; use &lt;code class=&quot;language-text&quot;&gt;parseInt&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;parseFloat&lt;/code&gt; to parse numbers, except for non-base-10 strings (see below). Both of those functions ignore trailing characters in the string, which can shadow error conditions (e.g. parsing &lt;code class=&quot;language-text&quot;&gt;12 dwarves&lt;/code&gt; as &lt;code class=&quot;language-text&quot;&gt;12&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someString&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Error prone,&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseFloat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// regardless of passing a radix.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Code that requires parsing with a radix &lt;em&gt;must&lt;/em&gt; check that its input contains only appropriate digits for that radix before calling into &lt;code class=&quot;language-text&quot;&gt;parseInt&lt;/code&gt;;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^[a-fA-F0-9]+$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Needed to parse hexadecimal.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// tslint:disable-next-line:ban&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someString&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Only allowed for radix != 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;Number()&lt;/code&gt; followed by &lt;code class=&quot;language-text&quot;&gt;Math.floor&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Math.trunc&lt;/code&gt; (where available) to parse integer numbers:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;someString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNaN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;implicit-coercion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implicit-coercion&quot; aria-label=&quot;implicit coercion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Implicit coercion&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Do not use explicit boolean coercions in conditional clauses that have implicit boolean coercion. Those are the conditions in an &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;while&lt;/code&gt; statements.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ Prefer: implicit coercion&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MyInterface&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ❌ Instead of explicit&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MyInterface&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Other types of values may be either implicitly coerced to booleans or compared explicitly with comparison operators:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Explicitly comparing &gt; 0 is OK:&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// so is relying on boolean coercion:&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;491-control-flow-statements-and-blocks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#491-control-flow-statements-and-blocks&quot; aria-label=&quot;491 control flow statements and blocks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.9.1 Control flow statements and blocks&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Control flow statements (&lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;else&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;do&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;while&lt;/code&gt;, etc) always use braced blocks for the containing code, even if the body contains only a single statement. The first statement of a non-empty block must begin on its own line.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doSomethingWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doSomethingWithALongMethodNameThatForcesANewLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doSomethingWithALongMethodNameThatForcesANewLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doSomethingWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Exception:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; statements fitting on one line &lt;em&gt;may&lt;/em&gt; elide the block.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;assignment-in-control-statements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#assignment-in-control-statements&quot; aria-label=&quot;assignment in control statements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Assignment in control statements&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Prefer to avoid assignment of variables inside control statements. Assignment can be easily mistaken for equality checks inside control statements.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Assignment easily mistaken with equality check&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In cases where assignment inside the control statement is preferred, enclose the assignment in additional parenthesis to indicate it is intentional.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Double parenthesis shows assignment is intentional&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;iterating-containers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#iterating-containers&quot; aria-label=&quot;iterating containers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Iterating containers&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Prefer &lt;code class=&quot;language-text&quot;&gt;for (... of someArr)&lt;/code&gt; to iterate over arrays. &lt;code class=&quot;language-text&quot;&gt;Array.prototype.forEach&lt;/code&gt; and vanilla &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; loops are also allowed:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; someArr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// x is a value of someArr.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; someArr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Explicitly count if the index is needed, otherwise use the for/of form.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; someArr&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; someArr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Alternative version of the above.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;-&lt;code class=&quot;language-text&quot;&gt;in&lt;/code&gt; loops may only be used on dict-style objects (see &lt;a href=&quot;https://google.github.io/styleguide/tsguide.html#optimization-compatibility-for-property-access&quot;&gt;below&lt;/a&gt; for more info). Do not use &lt;code class=&quot;language-text&quot;&gt;for (... in ...)&lt;/code&gt; to iterate over arrays as it will counterintuitively give the array’s indices (as strings!), not values:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; someArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// x is the index!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object.prototype.hasOwnProperty&lt;/code&gt; should be used in &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;-&lt;code class=&quot;language-text&quot;&gt;in&lt;/code&gt; loops to exclude unwanted prototype properties. Prefer &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;-&lt;code class=&quot;language-text&quot;&gt;of&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;Object.keys&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Object.values&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;Object.entries&lt;/code&gt; over &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;-&lt;code class=&quot;language-text&quot;&gt;in&lt;/code&gt; when possible.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doWork&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doWork&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doWorkValOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doWork&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;493-exception-handling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#493-exception-handling&quot; aria-label=&quot;493 exception handling permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.9.3 Exception handling&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Exceptions are an important part of the language and should be used whenever exceptional cases occur.&lt;/p&gt;
&lt;p&gt;Custom exceptions provide a great way to convey additional error information from functions. They should be defined and used wherever the native &lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt; type is insufficient.&lt;/p&gt;
&lt;p&gt;Prefer throwing exceptions over ad-hoc error-handling approaches (such as passing an error container reference type, or returning an object with an error property).&lt;/p&gt;
&lt;h3 id=&quot;instantiate-errors-usingnew&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#instantiate-errors-usingnew&quot; aria-label=&quot;instantiate errors usingnew permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Instantiate errors using &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Always use &lt;code class=&quot;language-text&quot;&gt;new Error()&lt;/code&gt; when instantiating exceptions, instead of just calling &lt;code class=&quot;language-text&quot;&gt;Error()&lt;/code&gt;. Both forms create a new &lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt; instance, but using &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; is more consistent with how other objects are instantiated.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ Prefer new Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Foo is not a valid bar.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ❌ Instead of just Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Foo is not a valid bar.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;only-throw-errors&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#only-throw-errors&quot; aria-label=&quot;only throw errors permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Only throw errors&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;JavaScript (and thus TypeScript) allow throwing or rejecting a Promise with arbitrary values. However if the thrown or rejected value is not an &lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt;, it does not populate stack trace information, making debugging hard. This treatment extends to &lt;code class=&quot;language-text&quot;&gt;Promise&lt;/code&gt; rejection values as &lt;code class=&quot;language-text&quot;&gt;Promise.reject(obj)&lt;/code&gt; is equivalent to &lt;code class=&quot;language-text&quot;&gt;throw obj;&lt;/code&gt; in async functions.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ bad: does not get a stack trace.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;oh noes!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// For promises&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;oh noes!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;oh noes!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead, only throw (subclasses of) &lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ Throw only Errors&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;oh noes!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ... or subtypes of Error.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyError&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;my oh noes!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// For promises&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// No reject is OK.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;oh noes!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;oh noes!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;catching-and-rethrowing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#catching-and-rethrowing&quot; aria-label=&quot;catching and rethrowing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Catching and rethrowing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;When catching errors, code &lt;em&gt;should&lt;/em&gt; assume that all thrown errors are instances of &lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assertIsError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;asserts&lt;/span&gt; e &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Error &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;e is not an Error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// All thrown errors must be Error subtypes. Do not handle&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// other possible values unless you know they are thrown.&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;assertIsError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;displayError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// or rethrow:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Exception handlers &lt;em&gt;must not defensively&lt;/em&gt; handle non-&lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt; types &lt;em&gt;unless&lt;/em&gt; the called API is conclusively known to throw non-&lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt;s in violation of the above rule. In that case, a comment should be included to specifically identify where the non-&lt;code class=&quot;language-text&quot;&gt;Error&lt;/code&gt;s originate.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;badApiThrowingStrings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Note: bad API throws strings instead of errors.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; e &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;string&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Avoid &lt;a href=&quot;https://en.wikipedia.org/wiki/Defensive_programming#Offensive_programming&quot;&gt;overly defensive programming&lt;/a&gt;. Repeating the same defenses against a problem that will not exist in most code leads to boiler-plate code that is not useful.&lt;/p&gt;
&lt;h2 id=&quot;494-switch-statements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#494-switch-statements&quot; aria-label=&quot;494 switch statements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.9.4 Switch statements&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;All &lt;code class=&quot;language-text&quot;&gt;switch&lt;/code&gt; statements &lt;em&gt;must&lt;/em&gt; contain a &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; statement group, even if it contains no code. The &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; statement group must be last.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doSomethingElse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// nothing to do.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Within a switch block, each statement group either terminates abruptly with a &lt;code class=&quot;language-text&quot;&gt;break&lt;/code&gt;, a &lt;code class=&quot;language-text&quot;&gt;return&lt;/code&gt; statement, or by throwing an exception. Non-empty statement groups (&lt;code class=&quot;language-text&quot;&gt;case ...&lt;/code&gt;) &lt;em&gt;must not&lt;/em&gt; fall through (enforced by the compiler):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// fall through - not allowed!&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Empty statement groups are allowed to fall through:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Y&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// nothing to do.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;495-equality-checks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#495-equality-checks&quot; aria-label=&quot;495 equality checks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.9.5 Equality checks&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Always use triple equals (&lt;code class=&quot;language-text&quot;&gt;===&lt;/code&gt;) and not equals (&lt;code class=&quot;language-text&quot;&gt;!==&lt;/code&gt;). The double equality operators cause error prone type coercions that are hard to understand and slower to implement for JavaScript Virtual Machines. See also the &lt;a href=&quot;https://dorey.github.io/JavaScript-Equality-Table/&quot;&gt;JavaScript equality table&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bar&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; bam&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Hard to understand behaviour due to type coercion.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bar&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; bam&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// All good here.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Exception:&lt;/strong&gt; Comparisons to the literal &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; value &lt;em&gt;may&lt;/em&gt; use the &lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;!=&lt;/code&gt; operators to cover both &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; values.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Will trigger when foo is null or undefined.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;496-type-and-non-nullability-assertions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#496-type-and-non-nullability-assertions&quot; aria-label=&quot;496 type and non nullability assertions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4.9.6 Type and non-nullability assertions&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Type assertions (&lt;code class=&quot;language-text&quot;&gt;x as SomeType&lt;/code&gt;) and non-nullability assertions (&lt;code class=&quot;language-text&quot;&gt;y!&lt;/code&gt;) are unsafe. Both only silence the TypeScript compiler, but do not insert any runtime checks to match these assertions, so they can cause your program to crash at runtime.&lt;/p&gt;
&lt;p&gt;Because of this, you &lt;em&gt;should not&lt;/em&gt; use type and non-nullability assertions without an obvious or explicit reason for doing so.&lt;/p&gt;
&lt;p&gt;Instead of the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; Foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

y&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you want to assert a type or non-nullability the best answer is to explicitly write a runtime check that performs that check.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// // ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// assuming Foo is a class.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  y&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sometimes due to some local property of your code you can be sure that the assertion form is safe. In those situations, you &lt;em&gt;should&lt;/em&gt; add clarification to explain why you are ok with the unsafe behavior:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// x is a Foo, because ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; Foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// y cannot be null, because ...&lt;/span&gt;
y&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the reasoning behind a type or non-nullability assertion is obvious, the comments &lt;em&gt;may&lt;/em&gt; not be necessary. For example, generated proto code is always nullable, but perhaps it is well-known in the context of the code that certain fields are always provided by the backend. Use your judgement.&lt;/p&gt;
&lt;h3 id=&quot;type-assertion-syntax&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#type-assertion-syntax&quot; aria-label=&quot;type assertion syntax permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Type assertion syntax&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Type assertions &lt;em&gt;must&lt;/em&gt; use the &lt;code class=&quot;language-text&quot;&gt;as&lt;/code&gt; syntax (as opposed to the angle brackets syntax). This enforces parentheses around the assertion when accessing a member.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ BAD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;z).length;
const y = &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;z.length;

// ✅ GOOD
// z must be Foo because ...
const x = (z as Foo).length;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;double-assertions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#double-assertions&quot; aria-label=&quot;double assertions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Double assertions&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;From the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions&quot;&gt;TypeScript handbook&lt;/a&gt;, TypeScript only allows type assertions which convert to a &lt;em&gt;more specific&lt;/em&gt; or &lt;em&gt;less specific&lt;/em&gt; version of a type. Adding a type assertion (&lt;code class=&quot;language-text&quot;&gt;x as Foo&lt;/code&gt;) which does not meet this criteria will give the error: &lt;code class=&quot;language-text&quot;&gt;Conversion of type &apos;X&apos; to type &apos;Y&apos; may be a mistake because neither type sufficiently overlaps with the other&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This rule prevents “impossible” coercions like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hello&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Conversion of type &apos;string&apos; to type &apos;number&apos; may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the  expression to &apos;unknown&apos; first.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are sure an assertion is safe, you can perform a &lt;em&gt;double assertion&lt;/em&gt;. This involves casting through &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; since it is less specific than all types.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// x is a Foo here, because...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; Foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fooMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; (instead of &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;{}&lt;/code&gt;) as the intermediate type.&lt;/p&gt;
&lt;h3 id=&quot;type-assertions-and-object-literals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#type-assertions-and-object-literals&quot; aria-label=&quot;type assertions and object literals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Type assertions and object literals&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Use type annotations (&lt;code class=&quot;language-text&quot;&gt;: Foo&lt;/code&gt;) instead of type assertions (&lt;code class=&quot;language-text&quot;&gt;as Foo&lt;/code&gt;) to specify the type of an object literal. This allows detecting refactoring bugs when the fields of an interface change over time.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ Do not use as&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  baz&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// was &quot;bam&quot;, but later renamed to &quot;baz&quot;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  bam&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;abc&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// no error!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; Foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    bam&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;abc&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// no error!&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; Foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ Use the type notation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  baz&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  bam&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;abc&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// complains about &quot;bam&quot; not being defined on Foo.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Foo &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    bam&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;abc&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// complains about &quot;bam&quot; not being defined on Foo.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;511-naming-style&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#511-naming-style&quot; aria-label=&quot;511 naming style permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;5.1.1 Naming style&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript expresses information in types, so names &lt;em&gt;should not&lt;/em&gt; be decorated with information that is included in the type. (See also &lt;a href=&quot;https://testing.googleblog.com/2017/10/code-health-identifiernamingpostforworl.html&quot;&gt;Testing Blog&lt;/a&gt; for more about what not to include.)&lt;/p&gt;
&lt;p&gt;Some concrete examples of this rule:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not use trailing or leading underscores for private properties or methods.&lt;/li&gt;
&lt;li&gt;Do not use the &lt;code class=&quot;language-text&quot;&gt;opt_&lt;/code&gt; prefix for optional parameters.
&lt;ul&gt;
&lt;li&gt;For accessors, see &lt;a href=&quot;https://google.github.io/styleguide/tsguide.html#getters-and-setters-accessors&quot;&gt;accessor rules&lt;/a&gt; below.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Do not mark interfaces specially (&lt;code class=&quot;language-text&quot;&gt;~~IMyInterface~~&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;~~MyFooInterface~~&lt;/code&gt;) unless it’s idiomatic in its environment. When introducing an interface for a class, give it a name that expresses why the interface exists in the first place (e.g. &lt;code class=&quot;language-text&quot;&gt;class TodoItem&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;interface TodoItemStorage&lt;/code&gt; if the interface expresses the format used for storage/serialization in JSON).&lt;/li&gt;
&lt;li&gt;Suffixing &lt;code class=&quot;language-text&quot;&gt;Observable&lt;/code&gt;s with &lt;code class=&quot;language-text&quot;&gt;$&lt;/code&gt; is a common external convention and can help resolve confusion regarding observable values vs concrete values. Judgement on whether this is a useful convention is left up to individual teams, but &lt;em&gt;should&lt;/em&gt; be consistent within projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;512-descriptive-names&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#512-descriptive-names&quot; aria-label=&quot;512 descriptive names permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;5.1.2 Descriptive names&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Names &lt;em&gt;must&lt;/em&gt; be descriptive and clear to a new reader. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Exception:&lt;/strong&gt; Variables that are in scope for 10 lines or fewer, including arguments that are &lt;em&gt;not&lt;/em&gt; part of an exported API, &lt;em&gt;may&lt;/em&gt; use short (e.g. single letter) variable names.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ Good identifiers:&lt;/span&gt;
errorCount          &lt;span class=&quot;token comment&quot;&gt;// No abbreviation.&lt;/span&gt;
dnsConnectionIndex  &lt;span class=&quot;token comment&quot;&gt;// Most people know what &quot;DNS&quot; stands for.&lt;/span&gt;
referrerUrl         &lt;span class=&quot;token comment&quot;&gt;// Ditto for &quot;URL&quot;.&lt;/span&gt;
customerId          &lt;span class=&quot;token comment&quot;&gt;// &quot;Id&quot; is both ubiquitous and unlikely to be misunderstood.&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Disallowed identifiers:&lt;/span&gt;
n                   &lt;span class=&quot;token comment&quot;&gt;// Meaningless.&lt;/span&gt;
nErr                &lt;span class=&quot;token comment&quot;&gt;// Ambiguous abbreviation.&lt;/span&gt;
nCompConns          &lt;span class=&quot;token comment&quot;&gt;// Ambiguous abbreviation.&lt;/span&gt;
wgcConnections      &lt;span class=&quot;token comment&quot;&gt;// Only your group knows what this stands for.&lt;/span&gt;
pcReader            &lt;span class=&quot;token comment&quot;&gt;// Lots of things can be abbreviated &quot;pc&quot;.&lt;/span&gt;
cstmrId             &lt;span class=&quot;token comment&quot;&gt;// Deletes internal letters.&lt;/span&gt;
kSecondsPerDay      &lt;span class=&quot;token comment&quot;&gt;// Do not use Hungarian notation.&lt;/span&gt;
customerID          &lt;span class=&quot;token comment&quot;&gt;// Incorrect camelcase of &quot;ID&quot;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;513-camel-case&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#513-camel-case&quot; aria-label=&quot;513 camel case permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;5.1.3 Camel case&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Treat abbreviations like acronyms in names as whole words&lt;/em&gt;, i.e. use &lt;code class=&quot;language-text&quot;&gt;loadHttpUrl&lt;/code&gt;, not &lt;code class=&quot;language-text&quot;&gt;~~loadHTTPURL~~&lt;/code&gt;, unless required by a platform name (e.g. &lt;code class=&quot;language-text&quot;&gt;XMLHttpRequest&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&quot;52-rules-by-identifier-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#52-rules-by-identifier-type&quot; aria-label=&quot;52 rules by identifier type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;5.2 Rules by identifier type&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Most identifier names should follow the casing in the table below, based on the identifier’s type.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Style&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Category&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;UpperCamelCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;class / interface / type / enum / decorator / type parameters / component functions in TSX / JSXElement type parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;lowerCamelCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;variable / parameter / function / method / property / module alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;CONSTANT_CASE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;global constant values, including enum values. See &lt;a href=&quot;https://google.github.io/styleguide/tsguide.html#identifiers-constants&quot;&gt;Constants&lt;/a&gt; below.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;#ident&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;private identifiers are never used.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;523-_prefixsuffix&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#523-_prefixsuffix&quot; aria-label=&quot;523 _prefixsuffix permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;5.2.3 &lt;code class=&quot;language-text&quot;&gt;_&lt;/code&gt; prefix/suffix&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Identifiers must not use &lt;code class=&quot;language-text&quot;&gt;_&lt;/code&gt; as a prefix or suffix.&lt;/p&gt;
&lt;p&gt;This also means that &lt;code class=&quot;language-text&quot;&gt;_&lt;/code&gt; &lt;em&gt;must not&lt;/em&gt; be used as an identifier by itself (e.g. to indicate a parameter is unused).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: If you only need some of the elements from an array (or TypeScript tuple), you can insert extra commas in a destructuring statement to ignore in-between elements:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// a &amp;lt;- 1, b &amp;lt;- 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;525-constants&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#525-constants&quot; aria-label=&quot;525 constants permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;5.2.5 Constants&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Immutable&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;CONSTANT_CASE&lt;/code&gt; indicates that a value is &lt;em&gt;intended&lt;/em&gt; to not be changed, and &lt;em&gt;may&lt;/em&gt; be used for values that can technically be modified (i.e. values that are not deeply frozen) to indicate to users that they must not be modified.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UNIT_SUFFIXES&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&apos;milliseconds&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ms&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&apos;seconds&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;s&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Even though per the rules of JavaScript UNIT_SUFFIXES is&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// mutable, the uppercase shows users to not modify it.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A constant can also be a &lt;code class=&quot;language-text&quot;&gt;static readonly&lt;/code&gt; property of a class.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MY_SPECIAL_NUMBER&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MY_SPECIAL_NUMBER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Global&lt;/strong&gt;: Only symbols declared on the module level, static fields of module level classes, and values of module level enums, &lt;em&gt;may&lt;/em&gt; use &lt;code class=&quot;language-text&quot;&gt;CONST_CASE&lt;/code&gt;. If a value can be instantiated more than once over the lifetime of the program (e.g. a local variable declared within a function, or a static field on a class nested in a function) then it &lt;em&gt;must&lt;/em&gt; use &lt;code class=&quot;language-text&quot;&gt;lowerCamelCase&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;62-undefined-and-null&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#62-undefined-and-null&quot; aria-label=&quot;62 undefined and null permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;6.2. Undefined and null&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript supports &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; types. Nullable types can be constructed as a union type (&lt;code class=&quot;language-text&quot;&gt;string|null&lt;/code&gt;); similarly with &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;. There is no special syntax for unions of &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;TypeScript code can use either &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; to denote absence of a value, there is no general guidance to prefer one over the other. Many JavaScript APIs use &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; (e.g. &lt;code class=&quot;language-text&quot;&gt;Map.get&lt;/code&gt;), while many DOM and Google APIs use &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; (e.g. &lt;code class=&quot;language-text&quot;&gt;Element.getAttribute&lt;/code&gt;), so the appropriate absent value depends on the context.&lt;/p&gt;
&lt;h3 id=&quot;621-nullableundefined-type-aliases&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#621-nullableundefined-type-aliases&quot; aria-label=&quot;621 nullableundefined type aliases permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;6.2.1 Nullable/undefined type aliases&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Type aliases &lt;em&gt;must not&lt;/em&gt; include &lt;code class=&quot;language-text&quot;&gt;|null&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;|undefined&lt;/code&gt; in a union type. Nullable aliases typically indicate that null values are being passed around through too many layers of an application, and this clouds the source of the original issue that resulted in &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;. They also make it unclear when specific values on a class or interface might be absent.&lt;/p&gt;
&lt;p&gt;Instead, code &lt;em&gt;must&lt;/em&gt; only add &lt;code class=&quot;language-text&quot;&gt;|null&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;|undefined&lt;/code&gt; when the alias is actually used. Code &lt;em&gt;should&lt;/em&gt; deal with null values close to where they arise, using the above techniques.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ Bad&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoffeeResponse&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Latte&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;Americano&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoffeeService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;getLatte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CoffeeResponse &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✅ Better&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoffeeResponse&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Latte&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;Americano&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoffeeService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;getLatte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CoffeeResponse&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;622-prefer-optional-overundefined&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#622-prefer-optional-overundefined&quot; aria-label=&quot;622 prefer optional overundefined permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;6.2.2 Prefer optional over &lt;code class=&quot;language-text&quot;&gt;|undefined&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In addition, TypeScript supports a special construct for optional parameters and fields, using &lt;code class=&quot;language-text&quot;&gt;?&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoffeeOrder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  sugarCubes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  milk&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Whole&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;LowFat&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;HalfHalf&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pourCoffee&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;volume&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Milliliter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Optional parameters implicitly include &lt;code class=&quot;language-text&quot;&gt;|undefined&lt;/code&gt; in their type. However, they are different in that they can be left out when constructing a value or calling a method. For example, &lt;code class=&quot;language-text&quot;&gt;{sugarCubes: 1}&lt;/code&gt; is a valid &lt;code class=&quot;language-text&quot;&gt;CoffeeOrder&lt;/code&gt; because &lt;code class=&quot;language-text&quot;&gt;milk&lt;/code&gt; is optional.&lt;/p&gt;
&lt;p&gt;Use optional fields (on interfaces or classes) and parameters rather than a &lt;code class=&quot;language-text&quot;&gt;|undefined&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;For classes preferably avoid this pattern altogether and initialize as many fields as possible.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyClass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  field &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;64-prefer-interfaces-over-type-literal-aliases&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#64-prefer-interfaces-over-type-literal-aliases&quot; aria-label=&quot;64 prefer interfaces over type literal aliases permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;6.4 Prefer interfaces over type literal aliases&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;TypeScript supports &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases&quot;&gt;type aliases&lt;/a&gt; for naming a type expression. This can be used to name primitives, unions, tuples, and any other types.&lt;/p&gt;
&lt;p&gt;However, when declaring types for objects, use &lt;em&gt;interfaces&lt;/em&gt; instead of a type alias for the object literal expression.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ Prefer interface&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ❌ Over type&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt;&lt;/span&gt; User &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;These forms are nearly equivalent, so under the principle of just choosing one out of two forms to prevent variation, we should choose one. Additionally, there are also &lt;a href=&quot;https://ncjamieson.com/prefer-interfaces/&quot;&gt;interesting technical reasons to prefer interface&lt;/a&gt;. That page quotes the TypeScript team lead: Honestly, my take is that it should really just be interfaces for anything that they can model. There is no benefit to type aliases when there are so many issues around display/perf.&lt;/p&gt;
&lt;h2 id=&quot;67-mapped-and-conditional-types&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#67-mapped-and-conditional-types&quot; aria-label=&quot;67 mapped and conditional types permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;6.7 Mapped and conditional types&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript’s &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/mapped-types.html&quot;&gt;mapped types&lt;/a&gt; and &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/conditional-types.html&quot;&gt;conditional types&lt;/a&gt; allow specifying new types based on other types. TypeScript’s standard library includes several type operators based on these (&lt;code class=&quot;language-text&quot;&gt;Record&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Partial&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Readonly&lt;/code&gt; etc).&lt;/p&gt;
&lt;p&gt;These type system features allow succinctly specifying types and constructing powerful yet type safe abstractions. They come with a number of drawbacks though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Compared to explicitly specifying properties and type relations (e.g. using interfaces and extension, see below for an example), type operators require the reader to mentally evaluate the type expression. This can make programs substantially harder to read, in particular combined with type inference and expressions crossing file boundaries.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mapped &amp;#x26; conditional types’ evaluation model, in particular when combined with type inference, is underspecified, not always well understood, and often subject to change in TypeScript compiler versions. Code can  compile or seem to give the right results. This increases future support cost of code using type operators.&lt;/p&gt;
&lt;p&gt;accidentally&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mapped &amp;#x26; conditional types are most powerful when deriving types from complex and/or inferred types. On the flip side, this is also when they are most prone to create hard to understand and maintain programs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Some language tooling does not work well with these type system features. E.g. your IDE’s find references (and thus rename property refactoring) will not find properties in a &lt;code class=&quot;language-text&quot;&gt;Pick&amp;lt;T, Keys&gt;&lt;/code&gt; type, and Code Search won’t hyperlink them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The style recommendation is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always use the simplest type construct that can possibly express your code.&lt;/li&gt;
&lt;li&gt;A little bit of repetition or verbosity is often much cheaper than the long term cost of complex type expressions.&lt;/li&gt;
&lt;li&gt;Mapped &amp;#x26; conditional types may be used, subject to these considerations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, TypeScript’s builtin &lt;code class=&quot;language-text&quot;&gt;Pick&amp;lt;T, Keys&gt;&lt;/code&gt; type allows creating a new type by subsetting another type &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;, but simple interface extension can often be easier to understand.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  shoeSize&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  favoriteIcecream&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  favoriteChocolate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ❌ FoodPreferences has favoriteIcecream and favoriteChocolate, but not shoeSize.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FoodPreferences&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Pick&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;User&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;favoriteIcecream&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;favoriteChocolate&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is equivalent to spelling out the properties on &lt;code class=&quot;language-text&quot;&gt;FoodPreferences&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ GOOD&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FoodPreferences&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  favoriteIcecream&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  favoriteChocolate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To reduce duplication, &lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt; could extend &lt;code class=&quot;language-text&quot;&gt;FoodPreferences&lt;/code&gt;, or (possibly better) nest a field for food preferences:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FoodPreferences&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* as above */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FoodPreferences&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  shoeSize&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// also includes the preferences.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using interfaces here makes the grouping of properties explicit, improves IDE support, allows better optimization, and arguably makes the code easier to understand.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Typescript types vs interface]]></title><link>https://trungvose.comts-types-interface/</link><guid isPermaLink="false">https://trungvose.comts-types-interface/</guid><pubDate>Thu, 18 Dec 2025 07:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;tldr&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tldr&quot; aria-label=&quot;tldr permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Prefer interface for public object shapes, APIs, and models meant to be extended
&lt;ul&gt;
&lt;li&gt;Interface supports declaration merging and open extension&lt;/li&gt;
&lt;li&gt;Interface + extends is preferred over type + intersections for better performance and clearer errors&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use type when you need unions, especially discriminated unions
&lt;ul&gt;
&lt;li&gt;Type aliases are closed once defined and cannot be re-opened&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Both type and interface exist only in type space and are erased at runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;different-between-type-alias-and-interface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#different-between-type-alias-and-interface&quot; aria-label=&quot;different between type alias and interface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Different between type alias and interface&lt;/h2&gt;
&lt;p&gt;Read the original from &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces&quot;&gt;TypeScript handbook&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an &lt;code class=&quot;language-text&quot;&gt;interface&lt;/code&gt; are available in &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt;, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.&lt;/p&gt;
&lt;h3 id=&quot;interface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#interface&quot; aria-label=&quot;interface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Interface&lt;/h3&gt;
&lt;p&gt;Extending an interface&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Animal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bear&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Animal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  honey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bear &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bear&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bear&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;honey&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Adding new fields to an existing interface (declaration merging)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ts&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TypeScriptAPI&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;const a = &quot;Hello World&quot;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transpileModule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;types&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#types&quot; aria-label=&quot;types permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Types&lt;/h3&gt;
&lt;p&gt;Extending a type via intersections&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Animal&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bear&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Animal &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
  honey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bear &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getBear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bear&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
bear&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;honey&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A type cannot be changed after being created&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Window&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ts&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TypeScriptAPI&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error: Duplicate identifier &apos;Window&apos;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But type can do union, especially discriminated union&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkLoadingState&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;loading&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkFailedState&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  code&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkSuccessState&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  response&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    duration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    summary&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Create a type which represents only one of the above types&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// but you aren&apos;t sure which it is yet.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NetworkState&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; NetworkLoadingState
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; NetworkFailedState
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; NetworkSuccessState&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance&quot; aria-label=&quot;performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/microsoft/Typescript/wiki/Performance#preferring-interfaces-over-intersections&quot;&gt;microsoft/Typescript/wiki/Performance#preferring-interfaces-over-intersections&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Much of the time, a simple type alias to an object type acts very similarly to an interface.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  prop&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; prop&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, and as soon as you need to compose two or more types, you have the option of extending those types with an interface, or intersecting them in a type alias, and that’s when the differences start to matter.&lt;/p&gt;
&lt;p&gt;Interfaces create a single flat object type that detects property conflicts, which are usually important to resolve! Intersections on the other hand just recursively merge properties, and in some cases produce &lt;code class=&quot;language-text&quot;&gt;never&lt;/code&gt;. Interfaces also display consistently better, whereas type aliases to intersections can’t be displayed in part of other intersections. Type relationships between interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy difference is that when checking against a target intersection type, every constituent is checked before checking against the “effective”/“flattened” type.&lt;/p&gt;
&lt;p&gt;For this reason, extending types with &lt;code class=&quot;language-text&quot;&gt;interface&lt;/code&gt;s/&lt;code class=&quot;language-text&quot;&gt;extends&lt;/code&gt; is suggested over creating intersection types.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; type Foo = Bar &amp;amp; Baz &amp;amp; {
&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;     someProp: string;
&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; }
&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; interface Foo extends Bar, Baz {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     someProp: string;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;interface vs. type doesn’t matter. They’re almost interchangeable. Interfaces can be merged, types are needed for unions, but those are basically the only differences, so just pick one and go with it. &lt;a href=&quot;https://www.reddit.com/r/typescript/comments/qc6u5d/whats_the_best_practice_for_when_to_use_interface/&quot;&gt; - reddit -&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Upgrade to Angular 20 from Angular 13 - Part 2: Angular 15]]></title><link>https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part2-angular-15/</link><guid isPermaLink="false">https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part2-angular-15/</guid><pubDate>Sat, 19 Jul 2025 05:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Time really flies! It has been almost a month since I last worked on upgrading my &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; from Angular 13 to 14. Since then, I have been to quite a few exciting events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/talks/2025-07-12-google-io-extended-mientrung/&quot;&gt;Google I/O Extended MienTrung 2025&lt;/a&gt;, where I had the chance to connect with the developer community in Vietnam 🇻🇳&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/posts/trungvose_i-have-been-incredibly-lucky-throughout-my-activity-7351851995667816448-q-bA&quot;&gt;SCS Youth Training&lt;/a&gt;, where I was a Lead Trainer supporting a group of aspiring Digital Advocates as they prepared to teach digital skills to the public&lt;/li&gt;
&lt;li&gt;And on top of that, work has been packed with new projects and challenges&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But now I’m back to continue our Angular migration journey! In &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/&quot;&gt;Part 1&lt;/a&gt;, we successfully upgraded from Angular 13 to Angular 14, navigating through various dependency conflicts with ESLint, custom webpack configurations, Ant Design packages, and even Sentry integration. We learned some valuable lessons about using &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; flags strategically and checking package compatibility with &lt;code class=&quot;language-text&quot;&gt;npm view&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;quick-recap-what-we-accomplished-in-part-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#quick-recap-what-we-accomplished-in-part-1&quot; aria-label=&quot;quick recap what we accomplished in part 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Quick Recap: What We Accomplished in Part 1&lt;/h2&gt;
&lt;p&gt;For those who missed Part 1, here’s what we tackled:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Started with Angular 13&lt;/strong&gt; and planned the incremental upgrade path: &lt;code class=&quot;language-text&quot;&gt;13 → 14 → 15 → 16 → 17 → 18 → 19 → 20&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Upgraded to Angular 14&lt;/strong&gt; using:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ng update @angular/core@14 @angular/cli@14 &lt;span class=&quot;token parameter variable&quot;&gt;--force&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Resolved dependency conflicts&lt;/strong&gt; with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-eslint/schematics&lt;/code&gt; (upgraded to 14.3.0)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt; (upgraded to 14.1.0)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@ant-design/icons-angular&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt; (upgraded to 14.1.0)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@sentry/angular&lt;/code&gt; (upgraded to 9.30.0)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fixed build issues&lt;/strong&gt; by removing the deprecated &lt;code class=&quot;language-text&quot;&gt;--sourceMap=true&lt;/code&gt; flag&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Successfully deployed&lt;/strong&gt; to Netlify with a working Angular 14 application&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key takeaway was that incremental upgrades are the way to go, and it’s perfectly fine to use &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; during major version transitions as long as you resolve all dependencies before merging.&lt;/p&gt;
&lt;h2 id=&quot;the-next-challenge-angular-14--15&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-next-challenge-angular-14--15&quot; aria-label=&quot;the next challenge angular 14  15 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Next Challenge: Angular 14 → 15&lt;/h2&gt;
&lt;p&gt;Now it’s time to tackle the next step in our migration journey. Please note that for now, I will upgrade the Angular version first before running any schematics for migrating to &lt;a href=&quot;https://angular.dev/reference/migrations/standalone&quot;&gt;standalone component&lt;/a&gt; and &lt;a href=&quot;https://angular.dev/reference/migrations/control-flow&quot;&gt;control flow&lt;/a&gt; (available in Angular v17).&lt;/p&gt;
&lt;p&gt;With the lessons learned from Part 1, the upgrade to Angular 15 should be easier to tackle.&lt;/p&gt;
&lt;h3 id=&quot;create-a-new-branch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#create-a-new-branch&quot; aria-label=&quot;create a new branch permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Create a New Branch&lt;/h3&gt;
&lt;p&gt;First things first, I created a new branch to safely experiment:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt; trung/v20-migration-part-2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;step-1-ng-update&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-ng-update&quot; aria-label=&quot;step 1 ng update permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: ng update&lt;/h2&gt;
&lt;p&gt;I started with the Angular 15 upgrade:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ng update @angular/core@15 @angular/cli@15&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you see the error &lt;code class=&quot;language-text&quot;&gt;zsh: command not found: ng&lt;/code&gt;, it means you don’t have the Angular CLI installed globally. You have two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install it globally&lt;/strong&gt; (recommended for frequent Angular development):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; @angular/cli&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Guide: &lt;a href=&quot;https://angular.dev/installation#install-angular-cli&quot;&gt;https://angular.dev/installation#install-angular-cli&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use npx&lt;/strong&gt; (if you prefer not to install globally):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng update @angular/core@15 @angular/cli@15&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;npx&lt;/code&gt; command allows you to run an arbitrary command from an npm package (either installed locally or fetched remotely). If &lt;code class=&quot;language-text&quot;&gt;ng&lt;/code&gt; doesn’t exist on your machine, npm will fetch it from the registry and execute it.&lt;/p&gt;
&lt;h3 id=&quot;ng-update---force&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ng-update---force&quot; aria-label=&quot;ng update   force permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ng update —force&lt;/h3&gt;
&lt;p&gt;I ran the command and immediately hit an error that looked very familiar from &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/&quot;&gt;Part 1&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; npx ng update @angular/core@15 @angular/cli@15

The installed Angular CLI version is outdated.
Installing a temporary Angular CLI versioned &lt;span class=&quot;token number&quot;&gt;15.2&lt;/span&gt;.11 to perform the update.
✔ Packages successfully installed.
Using package manager: &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt;
Collecting installed dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
Found &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt; dependencies.
Fetching dependency metadata from registry&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
                  Package &lt;span class=&quot;token string&quot;&gt;&quot;@angular-eslint/schematics&quot;&lt;/span&gt; has an incompatible peer dependency to &lt;span class=&quot;token string&quot;&gt;&quot;@angular/cli&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requires &lt;span class=&quot;token string&quot;&gt;&quot;&gt;= 14.0.0 &amp;lt; 15.0.0&quot;&lt;/span&gt;, would &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.11&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;.
✖ Migration failed: Incompatible peer dependencies found.
Peer dependency warnings when installing dependencies means that those dependencies might not work correctly together.
You can use the &lt;span class=&quot;token string&quot;&gt;&apos;--force&apos;&lt;/span&gt; option to ignore incompatible peer dependencies and instead address these warnings later.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The issue is that ESLint requires a specific Angular version. We have a chicken-and-egg problem: if we upgrade ESLint first, it will require Angular 14, but if we upgrade Angular first, ESLint will complain. The solution is to use the &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; option to proceed with the Angular upgrade first, then upgrade the dependencies.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b9e29ae1a8dde6639115d0349b6ee1da/ac66c/angular-force-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHOVZYEB//EABYQAQEBAAAAAAAAAAAAAAAAAAEQAP/aAAgBAQABBQLMb//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAICAwAAAAAAAAAAAAAAAAEhABARMUH/2gAIAQEAAT8hLG8RO0Shr//aAAwDAQACAAMAAAAQQM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhMRBRkWH/2gAIAQEAAT8QSV6uiEyLPyooub9lGp5Erx//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;npx ng update @angular/core@15 @angular/cli@15 --force&quot;
        title=&quot;npx ng update @angular/core@15 @angular/cli@15 --force&quot;
        src=&quot;/static/b9e29ae1a8dde6639115d0349b6ee1da/6a068/angular-force-01.jpg&quot;
        srcset=&quot;/static/b9e29ae1a8dde6639115d0349b6ee1da/09b79/angular-force-01.jpg 240w,
/static/b9e29ae1a8dde6639115d0349b6ee1da/7cc5e/angular-force-01.jpg 480w,
/static/b9e29ae1a8dde6639115d0349b6ee1da/6a068/angular-force-01.jpg 960w,
/static/b9e29ae1a8dde6639115d0349b6ee1da/644c5/angular-force-01.jpg 1440w,
/static/b9e29ae1a8dde6639115d0349b6ee1da/0f98f/angular-force-01.jpg 1920w,
/static/b9e29ae1a8dde6639115d0349b6ee1da/ac66c/angular-force-01.jpg 2402w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After running the command with &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt;, now all Angular versions are updated to v15.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1ed52a1869f1d348dcd505746ab67068/c29b3/angular-force-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgADBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAHmFZkoQ//EABcQAAMBAAAAAAAAAAAAAAAAAAABERD/2gAIAQEAAQUCSpCC3//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AVf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAVEAEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAQAGPwJr/8QAHBAAAgICAwAAAAAAAAAAAAAAAAERMRAhUXGB/9oACAEBAAE/IdjvD0UDbmyXyf/aAAwDAQACAAMAAAAQx+//xAAYEQACAwAAAAAAAAAAAAAAAAAAARFRYf/aAAgBAwEBPxBPCFH/xAAWEQEBAQAAAAAAAAAAAAAAAAAAASH/2gAIAQIBAT8QrX//xAAaEAEAAgMBAAAAAAAAAAAAAAABABEhQZFh/9oACAEBAAE/EK7zZQzF7GXhpQpmbR2ezs//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;package.json after upgrading&quot;
        title=&quot;package.json after upgrading&quot;
        src=&quot;/static/1ed52a1869f1d348dcd505746ab67068/6a068/angular-force-02.jpg&quot;
        srcset=&quot;/static/1ed52a1869f1d348dcd505746ab67068/09b79/angular-force-02.jpg 240w,
/static/1ed52a1869f1d348dcd505746ab67068/7cc5e/angular-force-02.jpg 480w,
/static/1ed52a1869f1d348dcd505746ab67068/6a068/angular-force-02.jpg 960w,
/static/1ed52a1869f1d348dcd505746ab67068/644c5/angular-force-02.jpg 1440w,
/static/1ed52a1869f1d348dcd505746ab67068/0f98f/angular-force-02.jpg 1920w,
/static/1ed52a1869f1d348dcd505746ab67068/c29b3/angular-force-02.jpg 2486w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-2-upgrading-angular-builderscustom-webpack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-upgrading-angular-builderscustom-webpack&quot; aria-label=&quot;step 2 upgrading angular builderscustom webpack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: Upgrading @angular-builders/custom-webpack&lt;/h2&gt;
&lt;p&gt;I committed the changes and tried running &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;, but encountered a compatibility issue:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error code ERESOLVE
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error ERESOLVE could not resolve
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error While resolving: @angular-builders/custom-webpack@14.1.0
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error Found: @angular/compiler-cli@15.2.10
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error node_modules/@angular/compiler-cli
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error   dev @angular/compiler-cli@&lt;span class=&quot;token string&quot;&gt;&quot;^15.2.10&quot;&lt;/span&gt; from the root project
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error   peer @angular/compiler-cli@&lt;span class=&quot;token string&quot;&gt;&quot;^15.0.0&quot;&lt;/span&gt; from @angular-devkit/build-angular@15.2.11
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error   node_modules/@angular-devkit/build-angular
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error     dev @angular-devkit/build-angular@&lt;span class=&quot;token string&quot;&gt;&quot;^15.2.11&quot;&lt;/span&gt; from the root project
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error     peer @angular-devkit/build-angular@&lt;span class=&quot;token string&quot;&gt;&quot;&gt;=0.8.9 || &gt;= 12.0.0&quot;&lt;/span&gt; from @storybook/angular@6.4.19
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error     node_modules/@storybook/angular
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error       dev @storybook/angular@&lt;span class=&quot;token string&quot;&gt;&quot;^6.1.11&quot;&lt;/span&gt; from the root project
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error       &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;@storybook/addon-docs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error   &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;@ngtools/webpack, @storybook/angular&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error Could not resolve dependency:
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error peer @angular/compiler-cli@&lt;span class=&quot;token string&quot;&gt;&quot;^14.0.0&quot;&lt;/span&gt; from @angular-builders/custom-webpack@14.1.0
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error node_modules/@angular-builders/custom-webpack
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error   dev @angular-builders/custom-webpack@&lt;span class=&quot;token string&quot;&gt;&quot;^14.1.0&quot;&lt;/span&gt; from the root project
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error Conflicting peer dependency: @angular/compiler-cli@14.3.0
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error node_modules/@angular/compiler-cli
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error   peer @angular/compiler-cli@&lt;span class=&quot;token string&quot;&gt;&quot;^14.0.0&quot;&lt;/span&gt; from @angular-builders/custom-webpack@14.1.0
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error   node_modules/@angular-builders/custom-webpack
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error     dev @angular-builders/custom-webpack@&lt;span class=&quot;token string&quot;&gt;&quot;^14.1.0&quot;&lt;/span&gt; from the root project
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error Fix the upstream dependency conflict, or retry
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error this &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; with &lt;span class=&quot;token parameter variable&quot;&gt;--force&lt;/span&gt; or --legacy-peer-deps
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; error to accept an incorrect &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;and potentially broken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; dependency resolution.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This npm error occurs because &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack@14.1.0&lt;/code&gt; requires Angular 14’s &lt;code class=&quot;language-text&quot;&gt;@angular/compiler-cli&lt;/code&gt;, but after upgrading to Angular 15, the project now has Angular 15’s &lt;code class=&quot;language-text&quot;&gt;@angular/compiler-cli@15.2.10&lt;/code&gt;, creating a version conflict that npm cannot automatically resolve.&lt;/p&gt;
&lt;p&gt;Which means we need to also upgrade &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt; to a newer version.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/663dc0af09d039e697ecf49f50bc16f8/69ab3/custom-webpack-install.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHMXdYEB//EABYQAAMAAAAAAAAAAAAAAAAAABARIP/aAAgBAQABBQIOP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAAAABEBEhUf/aAAgBAQABPyG8GW7Dj//aAAwDAQACAAMAAAAQM8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAQACAwAAAAAAAAAAAAABEQAQITFhcf/aAAgBAQABPxBq1b90294EAcPevWbfPH//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Upgrading Custom Webpack&quot;
        title=&quot;Upgrading Custom Webpack&quot;
        src=&quot;/static/663dc0af09d039e697ecf49f50bc16f8/6a068/custom-webpack-install.jpg&quot;
        srcset=&quot;/static/663dc0af09d039e697ecf49f50bc16f8/09b79/custom-webpack-install.jpg 240w,
/static/663dc0af09d039e697ecf49f50bc16f8/7cc5e/custom-webpack-install.jpg 480w,
/static/663dc0af09d039e697ecf49f50bc16f8/6a068/custom-webpack-install.jpg 960w,
/static/663dc0af09d039e697ecf49f50bc16f8/644c5/custom-webpack-install.jpg 1440w,
/static/663dc0af09d039e697ecf49f50bc16f8/0f98f/custom-webpack-install.jpg 1920w,
/static/663dc0af09d039e697ecf49f50bc16f8/69ab3/custom-webpack-install.jpg 2404w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To see what versions were available for &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt;, I ran:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token inserted-arrow inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;&gt;&lt;/span&gt; npm view @angular-builders/custom-webpack versions --json
&lt;/span&gt;
[
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ...
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;14.1.0&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;15.0.0-beta.0&quot;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  &quot;15.0.0&quot;,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;16.0.0-beta.0&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;16.0.0-beta.1&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;16.0.0-beta.2&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;16.0.0&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ...
&lt;/span&gt;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The list was long, but I could use version &lt;code class=&quot;language-text&quot;&gt;15.0.0&lt;/code&gt;. To verify compatibility, I checked the peer dependencies:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular-builders/custom-webpack@15.0.0 peerDependencies &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;@angular/compiler-cli&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^15.0.0&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I updated the &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; to use &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack@15.0.0&lt;/code&gt; and ran &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;. For sure, there are still a few dependencies we need to resolve before a normal npm install can run successfully. So I ran &lt;code class=&quot;language-text&quot;&gt;npm install --force&lt;/code&gt; first to generate a new &lt;code class=&quot;language-text&quot;&gt;package-lock.json&lt;/code&gt;, committed the changes, and moved on to the next error.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b5fcbc748dafc988a880787524ec915/ac66c/custom-webpack-force.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHNUxRJUP/EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAQACAgMAAAAAAAAAAAAAAAEAIRAxQVGR/9oACAEBAAE/IaDb5FvmV2xjj//aAAwDAQACAAMAAAAQY8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAEAAgEFAQAAAAAAAAAAAAABABEhEDFRcYHh/9oACAEBAAE/EM8w3wYVqfVRflJte4lXT//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Upgrading Custom Webpack Force&quot;
        title=&quot;Upgrading Custom Webpack Force&quot;
        src=&quot;/static/4b5fcbc748dafc988a880787524ec915/6a068/custom-webpack-force.jpg&quot;
        srcset=&quot;/static/4b5fcbc748dafc988a880787524ec915/09b79/custom-webpack-force.jpg 240w,
/static/4b5fcbc748dafc988a880787524ec915/7cc5e/custom-webpack-force.jpg 480w,
/static/4b5fcbc748dafc988a880787524ec915/6a068/custom-webpack-force.jpg 960w,
/static/4b5fcbc748dafc988a880787524ec915/644c5/custom-webpack-force.jpg 1440w,
/static/4b5fcbc748dafc988a880787524ec915/0f98f/custom-webpack-force.jpg 1920w,
/static/4b5fcbc748dafc988a880787524ec915/ac66c/custom-webpack-force.jpg 2402w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-3-upgrade-angular-eslintschematics&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-upgrade-angular-eslintschematics&quot; aria-label=&quot;step 3 upgrade angular eslintschematics permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3. Upgrade @angular-eslint/schematics&lt;/h2&gt;
&lt;p&gt;After running &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; again, I saw that &lt;code class=&quot;language-text&quot;&gt;@angular-eslint/schematics&lt;/code&gt; also needed an upgrade.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bbf3f32a45587bbaa3a3e98429f30ff9/16845/eslint-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeZKkwwf/8QAFhAAAwAAAAAAAAAAAAAAAAAAEBEg/9oACAEBAAEFAg4//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEQESFR/9oACAEBAAE/IbwZbsOP/9oADAMBAAIAAwAAABBzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQACAwEAAAAAAAAAAAAAAAEAERAhMWH/2gAIAQEAAT8QbBVuEHTcBAFRPJ3j/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ESLint schematics upgrade&quot;
        title=&quot;ESLint schematics upgrade&quot;
        src=&quot;/static/bbf3f32a45587bbaa3a3e98429f30ff9/6a068/eslint-01.jpg&quot;
        srcset=&quot;/static/bbf3f32a45587bbaa3a3e98429f30ff9/09b79/eslint-01.jpg 240w,
/static/bbf3f32a45587bbaa3a3e98429f30ff9/7cc5e/eslint-01.jpg 480w,
/static/bbf3f32a45587bbaa3a3e98429f30ff9/6a068/eslint-01.jpg 960w,
/static/bbf3f32a45587bbaa3a3e98429f30ff9/644c5/eslint-01.jpg 1440w,
/static/bbf3f32a45587bbaa3a3e98429f30ff9/0f98f/eslint-01.jpg 1920w,
/static/bbf3f32a45587bbaa3a3e98429f30ff9/16845/eslint-01.jpg 2406w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I checked the available versions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token inserted-arrow inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;&gt;&lt;/span&gt; npm view @angular-eslint/schematics versions --json
&lt;/span&gt;
&quot;15.2.0&quot;,
&quot;15.2.1-alpha.0&quot;,
&quot;15.2.1-alpha.2&quot;,
&quot;15.2.1-alpha.5&quot;,
&quot;15.2.1-alpha.9&quot;,
&quot;15.2.1-alpha.13&quot;,
&quot;15.2.1-alpha.15&quot;,
&quot;15.2.1-alpha.18&quot;,
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &quot;15.2.1&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There were many versions available up to 20, so I picked &lt;code class=&quot;language-text&quot;&gt;15.2.1&lt;/code&gt; and updated my &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; for all &lt;code class=&quot;language-text&quot;&gt;@angular-eslint/*&lt;/code&gt; packages. After that, I ran:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--force&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Committed the changes and moved on — now it’s time to tackle &lt;code class=&quot;language-text&quot;&gt;@angular/cdk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/539f4/eslint-version.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgADBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHmBZwoR//EABkQAQACAwAAAAAAAAAAAAAAAAEAERASMv/aAAgBAQABBQILNZUOcf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAAAABESBRQf/aAAgBAQABPyHrjbSe0P/aAAwDAQACAAMAAAAQb/8A/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAERUWH/2gAIAQMBAT8QTwlUf//EABcRAQADAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8Qm2v/xAAbEAEAAgIDAAAAAAAAAAAAAAABABEQITFx8P/aAAgBAQABPxB7Gm4BSPBg27jy4//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ESLint schematics upgrade&quot;
        title=&quot;ESLint schematics upgrade&quot;
        src=&quot;/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/6a068/eslint-version.jpg&quot;
        srcset=&quot;/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/09b79/eslint-version.jpg 240w,
/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/7cc5e/eslint-version.jpg 480w,
/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/6a068/eslint-version.jpg 960w,
/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/644c5/eslint-version.jpg 1440w,
/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/0f98f/eslint-version.jpg 1920w,
/static/ed1e89ea20ed12f112ca1b4d9fc7b9d0/539f4/eslint-version.jpg 2374w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-4-upgrade-angularcdk&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-4-upgrade-angularcdk&quot; aria-label=&quot;step 4 upgrade angularcdk permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 4: Upgrade @angular/cdk&lt;/h2&gt;
&lt;p&gt;Running &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; again, we can see that @angular/cdk is now not compatible. I guess I forgot to update it in &lt;a href=&quot;/blog/jira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/&quot;&gt;Part 1&lt;/a&gt; but it was still compatible previously.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/eb5357979ac26d4718a50a3996d3ae6a/ac66c/cdk-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeZKsjAf/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAFxABAQEBAAAAAAAAAAAAAAAAARAAIf/aAAgBAQABPyFeZrP/2gAMAwEAAgADAAAAEPPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRABAQADAQAAAAAAAAAAAAAAAREAEDFB/9oACAEBAAE/EEVVtxIoLHt1Cc9zvX//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Upgrade @angular/cdk&quot;
        title=&quot;Upgrade @angular/cdk&quot;
        src=&quot;/static/eb5357979ac26d4718a50a3996d3ae6a/6a068/cdk-01.jpg&quot;
        srcset=&quot;/static/eb5357979ac26d4718a50a3996d3ae6a/09b79/cdk-01.jpg 240w,
/static/eb5357979ac26d4718a50a3996d3ae6a/7cc5e/cdk-01.jpg 480w,
/static/eb5357979ac26d4718a50a3996d3ae6a/6a068/cdk-01.jpg 960w,
/static/eb5357979ac26d4718a50a3996d3ae6a/644c5/cdk-01.jpg 1440w,
/static/eb5357979ac26d4718a50a3996d3ae6a/0f98f/cdk-01.jpg 1920w,
/static/eb5357979ac26d4718a50a3996d3ae6a/ac66c/cdk-01.jpg 2402w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We do the very similar process.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token inserted-arrow inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;&gt;&lt;/span&gt; npm view @angular/cdk versions --json
&lt;/span&gt;
&quot;15.2.0-next.2&quot;,
&quot;15.2.0-next.3&quot;,
&quot;15.2.0-next.4&quot;,
&quot;15.2.0-rc.0&quot;,
&quot;15.2.0&quot;,
&quot;15.2.1&quot;,
&quot;15.2.2&quot;,
&quot;15.2.3&quot;,
&quot;15.2.4&quot;,
&quot;15.2.5&quot;,
&quot;15.2.6&quot;,
&quot;15.2.7&quot;,
&quot;15.2.8&quot;,
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &quot;15.2.9&quot;,
&lt;/span&gt;&quot;16.0.0-next.0&quot;,
&quot;16.0.0-next.1&quot;,
&quot;16.0.0-next.2&quot;,
&quot;16.0.0-next.3&quot;,
&quot;16.0.0-next.4&quot;,&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There were many versions available up to 20, so I picked &lt;code class=&quot;language-text&quot;&gt;15.2.9&lt;/code&gt; and updated my &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; for all @angular/cdk. After that, I ran &lt;code class=&quot;language-text&quot;&gt;npm install --force&lt;/code&gt; as not all dependencies had been resolved. Committed the changes.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&quot;dependencies&quot;: {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;@angular/animations&quot;: &quot;^15.2.10&quot;,
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;  &quot;@angular/cdk&quot;: &quot;^13.2.4&quot;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  &quot;@angular/cdk&quot;: &quot;^15.2.9&quot;,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;@angular/common&quot;: &quot;^15.2.10&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;@angular/compiler&quot;: &quot;^15.2.10&quot;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;step-5-upgrading-ant-design-packages&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-5-upgrading-ant-design-packages&quot; aria-label=&quot;step 5 upgrading ant design packages permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 5: Upgrading Ant Design Packages&lt;/h2&gt;
&lt;p&gt;Next, &lt;code class=&quot;language-text&quot;&gt;@ant-design/icons-angular&lt;/code&gt; was not yet compatible.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/85c49aa4b741a51d853357b6a1e09b00/16845/ant-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.58333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeZKsgwf/8QAFhAAAwAAAAAAAAAAAAAAAAAAEBEg/9oACAEBAAEFAg4//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAATEAEBFR/9oACAEBAAE/ISoXNdouv//aAAwDAQACAAMAAAAQ88//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAwADAAAAAAAAAAAAAAABEBEhAFFh/9oACAEBAAE/EEunb7iBKFXAyvZv/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ant Design packages upgrade&quot;
        title=&quot;Ant Design packages upgrade&quot;
        src=&quot;/static/85c49aa4b741a51d853357b6a1e09b00/6a068/ant-01.jpg&quot;
        srcset=&quot;/static/85c49aa4b741a51d853357b6a1e09b00/09b79/ant-01.jpg 240w,
/static/85c49aa4b741a51d853357b6a1e09b00/7cc5e/ant-01.jpg 480w,
/static/85c49aa4b741a51d853357b6a1e09b00/6a068/ant-01.jpg 960w,
/static/85c49aa4b741a51d853357b6a1e09b00/644c5/ant-01.jpg 1440w,
/static/85c49aa4b741a51d853357b6a1e09b00/0f98f/ant-01.jpg 1920w,
/static/85c49aa4b741a51d853357b6a1e09b00/16845/ant-01.jpg 2406w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I followed the same process and checked available versions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token inserted-arrow inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;&gt;&lt;/span&gt; npm view @ant-design/icons-angular versions --json
&lt;/span&gt;
&quot;13.1.0&quot;,
&quot;14.1.0&quot;,
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &quot;15.0.0&quot;,
&lt;/span&gt;&quot;16.0.0-beta.0&quot;,
&quot;16.0.0&quot;,
&quot;17.0.0-beta.0&quot;,
&quot;17.0.0&quot;,&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I chose version &lt;code class=&quot;language-text&quot;&gt;15.0.0&lt;/code&gt; since it’s the closest version that should be compatible with Angular 15. I noticed &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt; needed upgrade as well. And surprisingly, after upgrading those two, running &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; did not show any error.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2e24b5f611b81fe8f1fff29226c0ee1d/16845/ant-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.74999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMCBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHl4tGCg//EABgQAAMBAQAAAAAAAAAAAAAAAAESIQAQ/9oACAEBAAEFAh1cYUn/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAgEBPwGn/8QAFhABAQEAAAAAAAAAAAAAAAAAEDEA/9oACAEBAAY/Aml3/8QAHBAAAgICAwAAAAAAAAAAAAAAAAERIWGRQVGx/9oACAEBAAE/IZJOnoefCHnRcLKpkVT5Lo//2gAMAwEAAgADAAAAEPv/AP/EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/EBn/xAAWEQADAAAAAAAAAAAAAAAAAAAQETH/2gAIAQIBAT8QcD//xAAcEAEAAgMAAwAAAAAAAAAAAAABABEhQVGBoeH/2gAIAQEAAT8QITZzKJHJHkXlgiLjTx9js4NxHC0Nae5//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ant Design versions&quot;
        title=&quot;Ant Design versions&quot;
        src=&quot;/static/2e24b5f611b81fe8f1fff29226c0ee1d/6a068/ant-02.jpg&quot;
        srcset=&quot;/static/2e24b5f611b81fe8f1fff29226c0ee1d/09b79/ant-02.jpg 240w,
/static/2e24b5f611b81fe8f1fff29226c0ee1d/7cc5e/ant-02.jpg 480w,
/static/2e24b5f611b81fe8f1fff29226c0ee1d/6a068/ant-02.jpg 960w,
/static/2e24b5f611b81fe8f1fff29226c0ee1d/644c5/ant-02.jpg 1440w,
/static/2e24b5f611b81fe8f1fff29226c0ee1d/0f98f/ant-02.jpg 1920w,
/static/2e24b5f611b81fe8f1fff29226c0ee1d/16845/ant-02.jpg 2406w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;done&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#done&quot; aria-label=&quot;done permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Done&lt;/h2&gt;
&lt;p&gt;Running &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; works perfectly as well ✅ Mission complete.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e191aedbaa9578974f131232629d75be/df51d/ng-serve-success.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB5mdJESL/AP/EABcQAAMBAAAAAAAAAAAAAAAAAAEQESD/2gAIAQEAAQUCoVx//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAEREDFR/9oACAEBAAE/IX0Y5YkUSj3j/9oADAMBAAIAAwAAABBEz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAQEBAAEFAAAAAAAAAAAAAAERAGEQITFRcf/aAAgBAQABPxDxG/G72h9rgwBQOdAKGcol+dP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ng serve success&quot;
        title=&quot;Ng serve success&quot;
        src=&quot;/static/e191aedbaa9578974f131232629d75be/6a068/ng-serve-success.jpg&quot;
        srcset=&quot;/static/e191aedbaa9578974f131232629d75be/09b79/ng-serve-success.jpg 240w,
/static/e191aedbaa9578974f131232629d75be/7cc5e/ng-serve-success.jpg 480w,
/static/e191aedbaa9578974f131232629d75be/6a068/ng-serve-success.jpg 960w,
/static/e191aedbaa9578974f131232629d75be/644c5/ng-serve-success.jpg 1440w,
/static/e191aedbaa9578974f131232629d75be/0f98f/ng-serve-success.jpg 1920w,
/static/e191aedbaa9578974f131232629d75be/df51d/ng-serve-success.jpg 2400w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The Angular 15 upgrade is complete. Compared to the Angular 14 upgrade, this round was much smoother and faster. Took about an hour, including screenshots and writing this post 🤣&lt;/p&gt;
&lt;p&gt;Here is the PR for reference: &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/107&quot;&gt;https://github.com/trungvose/jira-clone-angular/pull/107&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in Part 3: Angular 16.&lt;/p&gt;
&lt;h2 id=&quot;-angular-15-is-live-on-prod&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-angular-15-is-live-on-prod&quot; aria-label=&quot; angular 15 is live on prod permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ Angular 15 is live on PROD!&lt;/h2&gt;
&lt;p&gt;Merged and deployed successfully. Everything looks good.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a4ca82b7041a7aceb6579ab2503b9db7/d14d4/angular-15-deployed.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdqrBDD/xAAWEAADAAAAAAAAAAAAAAAAAAAQESD/2gAIAQEAAQUCCj//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAACAgMAAAAAAAAAAAAAAAAAARFBICGR/9oACAEBAAE/IVqpEk6XCGH/2gAMAwEAAgADAAAAEIAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBAAAQMFAAAAAAAAAAAAAAAAAQARMRAhQWGR/9oACAEBAAE/EABRdllaCpoHFEV//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 15 is live on PROD!&quot;
        title=&quot;Angular 15 is live on PROD!&quot;
        src=&quot;/static/a4ca82b7041a7aceb6579ab2503b9db7/6a068/angular-15-deployed.jpg&quot;
        srcset=&quot;/static/a4ca82b7041a7aceb6579ab2503b9db7/09b79/angular-15-deployed.jpg 240w,
/static/a4ca82b7041a7aceb6579ab2503b9db7/7cc5e/angular-15-deployed.jpg 480w,
/static/a4ca82b7041a7aceb6579ab2503b9db7/6a068/angular-15-deployed.jpg 960w,
/static/a4ca82b7041a7aceb6579ab2503b9db7/644c5/angular-15-deployed.jpg 1440w,
/static/a4ca82b7041a7aceb6579ab2503b9db7/d14d4/angular-15-deployed.jpg 1562w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Upgrade to Angular 20 from Angular 13 - Part 1: Angular 14]]></title><link>https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/</link><guid isPermaLink="false">https://trungvose.comjira-clone-upgrade-angular-13-to-angular-20-part1-angular-14/</guid><pubDate>Fri, 20 Jun 2025 15:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, &lt;a href=&quot;https://blog.angular.dev/announcing-angular-v20-b5c9c06cf301&quot;&gt;Angular 20&lt;/a&gt; has been released with tons of crazy cool features! While everyone’s talking about the major highlights, one of the less-discussed gems is the improved Angular Devtools that let you see simplified stats directly in Chrome DevTools.&lt;/p&gt;
&lt;p&gt;I was super excited to try it out, but instead of starting a fresh Angular 20 app, I thought about the applications I published years ago - &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt;, &lt;a href=&quot;https://github.com/trungvose/angular-tetris&quot;&gt;Tetris&lt;/a&gt;, and &lt;a href=&quot;https://github.com/trungvose/angular-spotify&quot;&gt;Angular Spotify&lt;/a&gt; - that are still running on Angular 10 or so 😂. This is the perfect moment to upgrade them to Angular 20 and then explore all the new features!&lt;/p&gt;
&lt;p&gt;So let’s start with migrating my &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Jira Clone&lt;/a&gt; from Angular 13 to Angular 20. Spoiler alert: it’s not as straightforward as I initially thought! 😅&lt;/p&gt;
&lt;h2 id=&quot;the-upgrade-strategy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-upgrade-strategy&quot; aria-label=&quot;the upgrade strategy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The upgrade strategy&lt;/h2&gt;
&lt;h3 id=&quot;create-a-new-branch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#create-a-new-branch&quot; aria-label=&quot;create a new branch permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Create a new branch&lt;/h3&gt;
&lt;p&gt;First things first, I created a new branch to safely experiment:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt; trung/v20-migration-part-1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;plan-the-upgrade-path&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#plan-the-upgrade-path&quot; aria-label=&quot;plan the upgrade path permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Plan the upgrade path&lt;/h3&gt;
&lt;p&gt;Angular does an excellent job guiding us through the upgrade process. I visited &lt;a href=&quot;https://angular.dev/update-guide?v=13.0-20.0&amp;#x26;l=2&quot;&gt;angular.dev/update-guide&lt;/a&gt; and selected version 13 as the starting point (what Jira Clone is currently running) and version 20 as the target.&lt;/p&gt;
&lt;p&gt;Initially, I thought Jira Clone was just a simple project that fetches data from a server and renders it on the client side. But the more I thought about it, I realized there were quite a few workarounds I implemented in Angular 13 to make certain features work. For example, I needed a &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/webpack.config.js#L6&quot;&gt;custom webpack configuration&lt;/a&gt; to process SCSS files through &lt;code class=&quot;language-text&quot;&gt;postcss-loader&lt;/code&gt; with several plugins:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.scss$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;postcssOptions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss-scss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;postcss-import&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tailwindcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;autoprefixer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This meant the upgrade from Angular 13 to Angular 20 might not be as smooth as I initially anticipated. So I chose “Medium” application complexity to get notified about the tricky steps.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/aea046ad5df96863e3553893618cbec6/f10e6/angular-update-path.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe4ho4x//8QAFhAAAwAAAAAAAAAAAAAAAAAAEBEg/9oACAEBAAEFAoY//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhAAAgIDAAAAAAAAAAAAAAAAAAERQRAhcf/aAAgBAQABPyG7E+4nYmJk/9oADAMBAAIAAwAAABDQz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQEAAgMBAAAAAAAAAAAAAAEAETEhQWGh/9oACAEBAAE/EABOcvINfRO5Y0DJKOofJv/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 13 to 20 upgrade path&quot;
        title=&quot;Angular 13 to 20 upgrade path&quot;
        src=&quot;/static/aea046ad5df96863e3553893618cbec6/6a068/angular-update-path.jpg&quot;
        srcset=&quot;/static/aea046ad5df96863e3553893618cbec6/09b79/angular-update-path.jpg 240w,
/static/aea046ad5df96863e3553893618cbec6/7cc5e/angular-update-path.jpg 480w,
/static/aea046ad5df96863e3553893618cbec6/6a068/angular-update-path.jpg 960w,
/static/aea046ad5df96863e3553893618cbec6/644c5/angular-update-path.jpg 1440w,
/static/aea046ad5df96863e3553893618cbec6/0f98f/angular-update-path.jpg 1920w,
/static/aea046ad5df96863e3553893618cbec6/f10e6/angular-update-path.jpg 2586w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-reality-check&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-reality-check&quot; aria-label=&quot;the reality check permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The reality check&lt;/h3&gt;
&lt;p&gt;As you can see from the suggested steps, the safest route is to upgrade incrementally: &lt;code class=&quot;language-text&quot;&gt;13 → 14 → 15 → 16 → 17 → 18 → 19 → 20&lt;/code&gt; 😂&lt;/p&gt;
&lt;p&gt;In a real production application, you’d plan these upgrades in advance and tackle one version per sprint. But since this is my side project, I’m going to do it all in one weekend morning (that didn’t happen - I only managed to migrate to Angular 14 in one morning and a bit of night time 🤣)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: After each upgrade step, test your changes and commit them if everything works. This way, if the next step fails, you can always roll back to the previous working version. Don’t try to do everything at once and commit at the end - you’ll never know which changes worked and which broke the upgrade.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2e590ccb49aacea69e26e39631b77a92/4a95b/angular-update-step.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHcephyA//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAMAAwAAAAAAAAAAAAAAAAABERAhcf/aAAgBAQABPyGdFilFs//aAAwDAQACAAMAAAAQM8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgIDAAAAAAAAAAAAAAABABExkUGh4f/aAAgBAQABPxCqt9vYiqjmKXjUGYrUdLZ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular upgrade steps&quot;
        title=&quot;Angular upgrade steps&quot;
        src=&quot;/static/2e590ccb49aacea69e26e39631b77a92/6a068/angular-update-step.jpg&quot;
        srcset=&quot;/static/2e590ccb49aacea69e26e39631b77a92/09b79/angular-update-step.jpg 240w,
/static/2e590ccb49aacea69e26e39631b77a92/7cc5e/angular-update-step.jpg 480w,
/static/2e590ccb49aacea69e26e39631b77a92/6a068/angular-update-step.jpg 960w,
/static/2e590ccb49aacea69e26e39631b77a92/644c5/angular-update-step.jpg 1440w,
/static/2e590ccb49aacea69e26e39631b77a92/0f98f/angular-update-step.jpg 1920w,
/static/2e590ccb49aacea69e26e39631b77a92/4a95b/angular-update-step.jpg 2598w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-1-ng-update&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-ng-update&quot; aria-label=&quot;step 1 ng update permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: ng update&lt;/h2&gt;
&lt;p&gt;I started with the Angular 14 upgrade:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ng update @angular/core@14 @angular/cli@14&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you see the error &lt;code class=&quot;language-text&quot;&gt;zsh: command not found: ng&lt;/code&gt;, it means you don’t have the Angular CLI installed globally. You have two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install it globally&lt;/strong&gt; (recommended for frequent Angular development):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; @angular/cli&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Guide: &lt;a href=&quot;https://angular.dev/installation#install-angular-cli&quot;&gt;https://angular.dev/installation#install-angular-cli&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use npx&lt;/strong&gt; (if you prefer not to install globally):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng update @angular/core@14 @angular/cli@14&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;npx&lt;/code&gt; command allows you to run an arbitrary command from an npm package (either installed locally or fetched remotely). If &lt;code class=&quot;language-text&quot;&gt;ng&lt;/code&gt; doesn’t exist on your machine, npm will fetch it from the registry and execute it.&lt;/p&gt;
&lt;h3 id=&quot;angular-eslintschematics-compatibility&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#angular-eslintschematics-compatibility&quot; aria-label=&quot;angular eslintschematics compatibility permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@angular-eslint/schematics compatibility&lt;/h3&gt;
&lt;p&gt;I ran the command and immediately hit an error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; npx ng update @angular/core@14 @angular/cli@14

The installed Angular CLI version is outdated.
Installing a temporary Angular CLI versioned &lt;span class=&quot;token number&quot;&gt;14.2&lt;/span&gt;.13 to perform the update.
✔ Package successfully installed.
Using package manager: &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt;
Collecting installed dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
Found &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt; dependencies.
Fetching dependency metadata from registry&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
                  Package &lt;span class=&quot;token string&quot;&gt;&quot;@angular-eslint/schematics&quot;&lt;/span&gt; has an incompatible peer dependency to &lt;span class=&quot;token string&quot;&gt;&quot;@angular/cli&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requires &lt;span class=&quot;token string&quot;&gt;&quot;&gt;= 13.0.0 &amp;lt; 14.0.0&quot;&lt;/span&gt;, would &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;14.2.13&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;.
✖ Migration failed: Incompatible peer dependencies found.
Peer dependency warnings when installing dependencies means that those dependencies might not work correctly together.
You can use the &lt;span class=&quot;token string&quot;&gt;&apos;--force&apos;&lt;/span&gt; option to ignore incompatible peer dependencies and instead address these warnings later.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The issue is that ESLint requires a specific Angular version. We have a chicken-and-egg problem: if we upgrade ESLint first, it will require Angular 14, but if we upgrade Angular first, ESLint will complain.&lt;/p&gt;
&lt;h3 id=&quot;ng-update---force&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ng-update---force&quot; aria-label=&quot;ng update   force permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ng update —force&lt;/h3&gt;
&lt;p&gt;The solution is to use the &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; option to proceed with the Angular upgrade first, then upgrade the dependencies.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c957105f1a2e357ccca8af75e75b1d9f/5db06/angular-update-force.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 73.33333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUBAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABX5xUbCsl/8QAHBAAAgEFAQAAAAAAAAAAAAAAAAECAwQQEiEi/9oACAEBAAEFAnJm8kW9XxhcP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABwQAAIABwAAAAAAAAAAAAAAAAACASAhIjJBkf/aAAgBAQAGPwLIo0elzbk//8QAGRABAQEAAwAAAAAAAAAAAAAAAQAhEVGR/9oACAEBAAE/IUGr2UQOPkXszJsnF//aAAwDAQACAAMAAAAQ4B//xAAWEQEBAQAAAAAAAAAAAAAAAAAAASH/2gAIAQMBAT8QS4//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAgEBPxBX/8QAGRABAQEBAQEAAAAAAAAAAAAAAREAMSGR/9oACAEBAAE/EKYLM+gYtSwtrLIZ3jzuRBPXANJ7zf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular update with force flag&quot;
        title=&quot;Angular update with force flag&quot;
        src=&quot;/static/c957105f1a2e357ccca8af75e75b1d9f/6a068/angular-update-force.jpg&quot;
        srcset=&quot;/static/c957105f1a2e357ccca8af75e75b1d9f/09b79/angular-update-force.jpg 240w,
/static/c957105f1a2e357ccca8af75e75b1d9f/7cc5e/angular-update-force.jpg 480w,
/static/c957105f1a2e357ccca8af75e75b1d9f/6a068/angular-update-force.jpg 960w,
/static/c957105f1a2e357ccca8af75e75b1d9f/644c5/angular-update-force.jpg 1440w,
/static/c957105f1a2e357ccca8af75e75b1d9f/0f98f/angular-update-force.jpg 1920w,
/static/c957105f1a2e357ccca8af75e75b1d9f/5db06/angular-update-force.jpg 2796w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-2-the-custom-webpack-dilemma&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-the-custom-webpack-dilemma&quot; aria-label=&quot;step 2 the custom webpack dilemma permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: The Custom Webpack Dilemma&lt;/h2&gt;
&lt;p&gt;After the Angular upgrade, I ran &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; and immediately encountered another error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;This version of CLI is only compatible with Angular versions ^13.0.0 &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; ^13.3.0-rc.0,
but Angular version &lt;span class=&quot;token number&quot;&gt;14.3&lt;/span&gt;.0 was found instead.

Please visit the &lt;span class=&quot;token function&quot;&gt;link&lt;/span&gt; below to &lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt; instructions on how to update Angular.
https://update.angular.io/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/242032cf7999b919d15b811e0f4da64a/f10e6/angular-update-cli.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAUC/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAZuGSmmpf//EABoQAAICAwAAAAAAAAAAAAAAAAASAQMCETH/2gAIAQEAAQUCeR8im2UNHD//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAaEAACAgMAAAAAAAAAAAAAAAAAAQIyICGR/9oACAEBAAY/ArMs+m5N4f/EABkQAAMBAQEAAAAAAAAAAAAAAAABETFBIf/aAAgBAQABPyF9xnixcsXo9ZVgr4p//9oADAMBAAIAAwAAABBE3//EABURAQEAAAAAAAAAAAAAAAAAAAAB/9oACAEDAQE/EEr/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhQTFRYaH/2gAIAQEAAT8QaV4bYfFSgw2y7aomYBx3MCxbHZYZ1P/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular CLI compatibility error&quot;
        title=&quot;Angular CLI compatibility error&quot;
        src=&quot;/static/242032cf7999b919d15b811e0f4da64a/6a068/angular-update-cli.jpg&quot;
        srcset=&quot;/static/242032cf7999b919d15b811e0f4da64a/09b79/angular-update-cli.jpg 240w,
/static/242032cf7999b919d15b811e0f4da64a/7cc5e/angular-update-cli.jpg 480w,
/static/242032cf7999b919d15b811e0f4da64a/6a068/angular-update-cli.jpg 960w,
/static/242032cf7999b919d15b811e0f4da64a/644c5/angular-update-cli.jpg 1440w,
/static/242032cf7999b919d15b811e0f4da64a/0f98f/angular-update-cli.jpg 1920w,
/static/242032cf7999b919d15b811e0f4da64a/f10e6/angular-update-cli.jpg 2586w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This was strange because the Angular CLI version was actually 14.2.13. I suspected the issue was with the &lt;code class=&quot;language-text&quot;&gt;@angular-devkit&lt;/code&gt; versions. Even though &lt;code class=&quot;language-text&quot;&gt;@angular-devkit/build-angular&lt;/code&gt; was at 14.2.13, other packages like &lt;code class=&quot;language-text&quot;&gt;@angular-devkit/core&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@angular-devkit/schematics&lt;/code&gt; were still at 13.2.5.&lt;/p&gt;
&lt;p&gt;To investigate which packages were using different versions of &lt;code class=&quot;language-text&quot;&gt;@angular-devkit/build-angular&lt;/code&gt;, I ran:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ls&lt;/span&gt; @angular-devkit/build-angular&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output revealed the culprit:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;jira-clone-angular-frontend@1.0.0 /Users/trung.vo/Source/jira-clone-angular
├─┬ @angular-builders/custom-webpack@13.1.0
│ └── @angular-devkit/build-angular@13.3.11
├── @angular-devkit/build-angular@14.2.13
└─┬ @storybook/angular@6.4.19
  └── @angular-devkit/build-angular@14.2.13 deduped&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack@13.1.0&lt;/code&gt; was using &lt;code class=&quot;language-text&quot;&gt;@angular-devkit/build-angular@13.3.11&lt;/code&gt;, so we needed to upgrade it.&lt;/p&gt;
&lt;h3 id=&quot;upgrading-angular-builderscustom-webpack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#upgrading-angular-builderscustom-webpack&quot; aria-label=&quot;upgrading angular builderscustom webpack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Upgrading @angular-builders/custom-webpack&lt;/h3&gt;
&lt;p&gt;To see what versions were available for &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt;, I ran:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular-builders/custom-webpack versions &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The list was long, but I could use version &lt;code class=&quot;language-text&quot;&gt;14.1.0&lt;/code&gt;. To verify compatibility, I checked the peer dependencies:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular-builders/custom-webpack@14.1.0 peerDependencies &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;@angular/compiler-cli&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^14.0.0&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Perfect! We definitely have &lt;code class=&quot;language-text&quot;&gt;compiler-cli&lt;/code&gt; 14 in our project, so I updated the &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; to use &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack@14.1.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b7b0d51c18c45b0590044b638e5bb469/cecf9/custom-webpack-version.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByYuAD//EABUQAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAEFAmt//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAFhAAAwAAAAAAAAAAAAAAAAAAEEFx/9oACAEBAAE/IVhWH//aAAwDAQACAAMAAAAQU8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAABBAMAAAAAAAAAAAAAAAAQAAERMVFxsf/aAAgBAQABPxC2gS+VxH//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Custom webpack versions&quot;
        title=&quot;Custom webpack versions&quot;
        src=&quot;/static/b7b0d51c18c45b0590044b638e5bb469/6a068/custom-webpack-version.jpg&quot;
        srcset=&quot;/static/b7b0d51c18c45b0590044b638e5bb469/09b79/custom-webpack-version.jpg 240w,
/static/b7b0d51c18c45b0590044b638e5bb469/7cc5e/custom-webpack-version.jpg 480w,
/static/b7b0d51c18c45b0590044b638e5bb469/6a068/custom-webpack-version.jpg 960w,
/static/b7b0d51c18c45b0590044b638e5bb469/644c5/custom-webpack-version.jpg 1440w,
/static/b7b0d51c18c45b0590044b638e5bb469/0f98f/custom-webpack-version.jpg 1920w,
/static/b7b0d51c18c45b0590044b638e5bb469/cecf9/custom-webpack-version.jpg 2010w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-3-upgrading-angular-eslint&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-upgrading-angular-eslint&quot; aria-label=&quot;step 3 upgrading angular eslint permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3: Upgrading @angular-eslint/*&lt;/h2&gt;
&lt;p&gt;After running &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; again, I saw that &lt;code class=&quot;language-text&quot;&gt;@angular-eslint/schematics@13.1.0&lt;/code&gt; also needed an upgrade.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f9027e20ff5abadf3813517c7534b147/cf91a/eslint-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABzICAf//EABYQAAMAAAAAAAAAAAAAAAAAAAABEP/aAAgBAQABBQIc/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAEQEWH/2gAIAQEAAT8hvRI//9oADAMBAAIAAwAAABCDz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQABBQEAAAAAAAAAAAAAAAEhABARMUFR/9oACAEBAAE/EFTLb2pMqr7b/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ESLint schematics upgrade&quot;
        title=&quot;ESLint schematics upgrade&quot;
        src=&quot;/static/f9027e20ff5abadf3813517c7534b147/6a068/eslint-01.jpg&quot;
        srcset=&quot;/static/f9027e20ff5abadf3813517c7534b147/09b79/eslint-01.jpg 240w,
/static/f9027e20ff5abadf3813517c7534b147/7cc5e/eslint-01.jpg 480w,
/static/f9027e20ff5abadf3813517c7534b147/6a068/eslint-01.jpg 960w,
/static/f9027e20ff5abadf3813517c7534b147/644c5/eslint-01.jpg 1440w,
/static/f9027e20ff5abadf3813517c7534b147/cf91a/eslint-01.jpg 1838w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I checked the available versions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @angular-eslint/schematics versions &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There were many versions available up to 20, so I picked &lt;code class=&quot;language-text&quot;&gt;14.3.0&lt;/code&gt; and updated my &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8f2ac73652d08bc03f3f4f806e6b0770/17460/eslint-version.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAHnnAKjF//EABoQAQABBQAAAAAAAAAAAAAAAAEAAhARIjH/2gAIAQEAAQUCpNUmIct//8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8Bqv/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAEFAAAAAAAAAAAAAAAAAAABESAhQf/aAAgBAQABPyFxBbkb/9oADAMBAAIAAwAAABATP//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAwEBPxAZbv/EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAgEBPxCxj//EABwQAAEFAAMAAAAAAAAAAAAAAAABESExcRCR4f/aAAgBAQABPxB4RL2eAYToo0W13j//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ESLint schematics upgrade&quot;
        title=&quot;ESLint schematics upgrade&quot;
        src=&quot;/static/8f2ac73652d08bc03f3f4f806e6b0770/6a068/eslint-version.jpg&quot;
        srcset=&quot;/static/8f2ac73652d08bc03f3f4f806e6b0770/09b79/eslint-version.jpg 240w,
/static/8f2ac73652d08bc03f3f4f806e6b0770/7cc5e/eslint-version.jpg 480w,
/static/8f2ac73652d08bc03f3f4f806e6b0770/6a068/eslint-version.jpg 960w,
/static/8f2ac73652d08bc03f3f4f806e6b0770/644c5/eslint-version.jpg 1440w,
/static/8f2ac73652d08bc03f3f4f806e6b0770/17460/eslint-version.jpg 1886w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-4-ant-designicons-angular-and-ng-zorro-antd&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-4-ant-designicons-angular-and-ng-zorro-antd&quot; aria-label=&quot;step 4 ant designicons angular and ng zorro antd permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 4: @ant-design/icons-angular and ng-zorro-antd&lt;/h2&gt;
&lt;p&gt;Next, &lt;code class=&quot;language-text&quot;&gt;@ant-design/icons-angular@13.1.0&lt;/code&gt; was causing issues. I followed the same process and checked available versions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @ant-design/icons-angular versions &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I chose version &lt;code class=&quot;language-text&quot;&gt;14.1.0&lt;/code&gt; since it’s the closest version that should be compatible with Angular 14. I also noticed &lt;code class=&quot;language-text&quot;&gt;ng-zorro-antd&lt;/code&gt; was on &lt;code class=&quot;language-text&quot;&gt;13.1.0&lt;/code&gt;, so I upgraded that as well.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/73a27fe77aa0933a201fe9ea2b62569e/9aa5e/ant-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEEAgP/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGDjtjcIT//xAAbEAABBAMAAAAAAAAAAAAAAAACAAESMgMEEf/aAAgBAQABBQInfsiWubwO42wU/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAgEFAQAAAAAAAAAAAAAAAAEhEDEyQYGR/9oACAEBAAY/AnOzJ+kt3HTp/8QAGxAAAgIDAQAAAAAAAAAAAAAAADEBERBBYaH/2gAIAQEAAT8hezbKkKRO11J7sTh//9oADAMBAAIAAwAAABB4z//EABYRAQEBAAAAAAAAAAAAAAAAABEQIf/aAAgBAwEBPxAxn//EABYRAAMAAAAAAAAAAAAAAAAAABARIf/aAAgBAgEBPxB0f//EAB4QAAIBAwUAAAAAAAAAAAAAAAABEXGhsSExUWHR/9oACAEBAAE/EIXFwdhvwlR/SeZTJNj0hFxybVGX/CP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ant Design packages upgrade&quot;
        title=&quot;Ant Design packages upgrade&quot;
        src=&quot;/static/73a27fe77aa0933a201fe9ea2b62569e/6a068/ant-01.jpg&quot;
        srcset=&quot;/static/73a27fe77aa0933a201fe9ea2b62569e/09b79/ant-01.jpg 240w,
/static/73a27fe77aa0933a201fe9ea2b62569e/7cc5e/ant-01.jpg 480w,
/static/73a27fe77aa0933a201fe9ea2b62569e/6a068/ant-01.jpg 960w,
/static/73a27fe77aa0933a201fe9ea2b62569e/644c5/ant-01.jpg 1440w,
/static/73a27fe77aa0933a201fe9ea2b62569e/0f98f/ant-01.jpg 1920w,
/static/73a27fe77aa0933a201fe9ea2b62569e/9aa5e/ant-01.jpg 2556w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/374d16ccd36a78fe9caea3c56d64fcff/0f6e2/ant-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMEBf/EABQBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAeelcdyRP//EAB0QAAECBwAAAAAAAAAAAAAAAAMBAgAREhMxMkH/2gAIAQEAAQUCqWV0sAIqs6zIdf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAICAwAAAAAAAAAAAAAAAAABEBEiMoH/2gAIAQEABj8CNmZO3c9P/8QAGhABAQACAwAAAAAAAAAAAAAAAQAxUUGBsf/aAAgBAQABPyE5Taj7mq+xN8rPP//aAAwDAQACAAMAAAAQUx//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEh/9oACAEDAQE/EAy4/8QAFhEBAQEAAAAAAAAAAAAAAAAAEQEQ/9oACAECAQE/EFpn/8QAGhAAAwADAQAAAAAAAAAAAAAAAAERIYGxof/aAAgBAQABPxBBWrVkNNBcVGcnIjsdZ63Ef//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ant Design versions&quot;
        title=&quot;Ant Design versions&quot;
        src=&quot;/static/374d16ccd36a78fe9caea3c56d64fcff/6a068/ant-02.jpg&quot;
        srcset=&quot;/static/374d16ccd36a78fe9caea3c56d64fcff/09b79/ant-02.jpg 240w,
/static/374d16ccd36a78fe9caea3c56d64fcff/7cc5e/ant-02.jpg 480w,
/static/374d16ccd36a78fe9caea3c56d64fcff/6a068/ant-02.jpg 960w,
/static/374d16ccd36a78fe9caea3c56d64fcff/644c5/ant-02.jpg 1440w,
/static/374d16ccd36a78fe9caea3c56d64fcff/0f6e2/ant-02.jpg 1716w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-5-sentryangular&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-5-sentryangular&quot; aria-label=&quot;step 5 sentryangular permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 5: @sentry/angular&lt;/h2&gt;
&lt;p&gt;Run &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; again and now &lt;code class=&quot;language-text&quot;&gt;@sentry/angular@6.18.1&lt;/code&gt; was throwing an error - which was expected! 😄 The tricky part with Sentry is that it doesn’t follow Angular’s versioning pattern (13, 14, etc.). Instead, it uses its own versioning like 6.18.1, so the next version that supports Angular 14 would likely be version 7.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5333c72334ca13840b31237094e9b5b5/4a9b5/sentry-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABzJAB/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGBABAQADAAAAAAAAAAAAAAAAAQARIVH/2gAIAQEAAT8hXU2Xrf/aAAwDAQACAAMAAAAQA8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAgMBAAAAAAAAAAAAAAABAFERITFB/9oACAEBAAE/EFVt7cB5AzARQs//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sentry version issue&quot;
        title=&quot;Sentry version issue&quot;
        src=&quot;/static/5333c72334ca13840b31237094e9b5b5/6a068/sentry-01.jpg&quot;
        srcset=&quot;/static/5333c72334ca13840b31237094e9b5b5/09b79/sentry-01.jpg 240w,
/static/5333c72334ca13840b31237094e9b5b5/7cc5e/sentry-01.jpg 480w,
/static/5333c72334ca13840b31237094e9b5b5/6a068/sentry-01.jpg 960w,
/static/5333c72334ca13840b31237094e9b5b5/644c5/sentry-01.jpg 1440w,
/static/5333c72334ca13840b31237094e9b5b5/0f98f/sentry-01.jpg 1920w,
/static/5333c72334ca13840b31237094e9b5b5/4a9b5/sentry-01.jpg 2358w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I checked the available Sentry versions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @sentry/angular versions &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/53e1b9f33b4fe329dec613c16a10232d/69357/sentry-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeTOyAf/xAAWEAEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQEAAQUCdrf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAEAAwEAAAAAAAAAAAAAAAABABAxkf/aAAgBAQABPyFB17FXVrV//9oADAMBAAIAAwAAABDTz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAgMAAAAAAAAAAAAAAAEAERBBIVFh/9oACAEBAAE/ELyHYbRa1PrLe4l5aDP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sentry versions&quot;
        title=&quot;Sentry versions&quot;
        src=&quot;/static/53e1b9f33b4fe329dec613c16a10232d/6a068/sentry-02.jpg&quot;
        srcset=&quot;/static/53e1b9f33b4fe329dec613c16a10232d/09b79/sentry-02.jpg 240w,
/static/53e1b9f33b4fe329dec613c16a10232d/7cc5e/sentry-02.jpg 480w,
/static/53e1b9f33b4fe329dec613c16a10232d/6a068/sentry-02.jpg 960w,
/static/53e1b9f33b4fe329dec613c16a10232d/644c5/sentry-02.jpg 1440w,
/static/53e1b9f33b4fe329dec613c16a10232d/69357/sentry-02.jpg 1770w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Wow, there were many versions! Sentry doesn’t jump per major version as you might expect. I searched for &lt;code class=&quot;language-text&quot;&gt;@sentry/angular compatible with angular 14&lt;/code&gt; and found comprehensive documentation from Sentry:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.sentry.io/platforms/javascript/guides/angular/manual-setup/#which-sdk-version-should-you-use-with-your-angular-version&quot;&gt;https://docs.sentry.io/platforms/javascript/guides/angular/manual-setup/#which-sdk-version-should-you-use-with-your-angular-version&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In its current major version, the Sentry Angular SDK supports Angular 14 and newer.&lt;/p&gt;
&lt;p&gt;If you’re using an older version of Angular, you also need to use an older version of the SDK. See the table below for compatibility guidance&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38ef935f6e2da70d60445a16c4bafccb/01a9b/sentry-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUCAwT/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAW1yaeXQJif/xAAcEAACAgIDAAAAAAAAAAAAAAACAwABBBETFCP/2gAIAQEAAQUCXXnqOZQGGbQj3hmQ3lZ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAgICAwAAAAAAAAAAAAAAAAEREgIQITJx/9oACAEBAAY/AsfNQVq+DoyUoP/EABsQAQEBAAIDAAAAAAAAAAAAAAERACExQVFh/9oACAEBAAE/ITbB0wPRucJxgyZMW6SeX7jrIJv/2gAMAwEAAgADAAAAEJMv/8QAFREBAQAAAAAAAAAAAAAAAAAAAAH/2gAIAQMBAT8QSv/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB4QAQACAQQDAAAAAAAAAAAAAAEAETEhQVFxgbHR/9oACAEBAAE/EGtTmS9oe/VDbwWjzAOhqNalQegZTpDVeL+z/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sentry compatibility guide&quot;
        title=&quot;Sentry compatibility guide&quot;
        src=&quot;/static/38ef935f6e2da70d60445a16c4bafccb/6a068/sentry-03.jpg&quot;
        srcset=&quot;/static/38ef935f6e2da70d60445a16c4bafccb/09b79/sentry-03.jpg 240w,
/static/38ef935f6e2da70d60445a16c4bafccb/7cc5e/sentry-03.jpg 480w,
/static/38ef935f6e2da70d60445a16c4bafccb/6a068/sentry-03.jpg 960w,
/static/38ef935f6e2da70d60445a16c4bafccb/644c5/sentry-03.jpg 1440w,
/static/38ef935f6e2da70d60445a16c4bafccb/01a9b/sentry-03.jpg 1710w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I had two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Install the latest version&lt;/strong&gt; using &lt;code class=&quot;language-text&quot;&gt;npm install @sentry/angular@latest --force&lt;/code&gt; (we need &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; because our dependencies aren’t fully resolved yet due to the Sentry package itself)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manually pick a compatible version&lt;/strong&gt; from the &lt;code class=&quot;language-text&quot;&gt;npm view&lt;/code&gt; output&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I chose option 2 and picked version &lt;code class=&quot;language-text&quot;&gt;9.30.0&lt;/code&gt;. To be safe, I checked the peer dependencies first:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; view @sentry/angular@9.30.0 peerDependencies &lt;span class=&quot;token parameter variable&quot;&gt;--json&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Excellent! The peer dependencies were quite broad: &lt;code class=&quot;language-text&quot;&gt;&gt;= 14.x &amp;lt;= 20.x&lt;/code&gt;, so we were safe to proceed.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/394c63acdd46e24033c6e740decf7651/cd367/sentry-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAEEBf/EABQBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAc+uBIX/xAAWEAEBAQAAAAAAAAAAAAAAAAARABD/2gAIAQEAAQUCMb//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAgEBPwGn/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFxABAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAQABPyHoe2wwy//aAAwDAQACAAMAAAAQD8//xAAWEQADAAAAAAAAAAAAAAAAAAABEDH/2gAIAQMBAT8QEX//xAAWEQEBAQAAAAAAAAAAAAAAAAABEDH/2gAIAQIBAT8Qdk//xAAZEAEAAwEBAAAAAAAAAAAAAAABABHRUXH/2gAIAQEAAT8QUQBZx2WqEgFeuz//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sentry compatibility guide&quot;
        title=&quot;Sentry compatibility guide&quot;
        src=&quot;/static/394c63acdd46e24033c6e740decf7651/6a068/sentry-04.jpg&quot;
        srcset=&quot;/static/394c63acdd46e24033c6e740decf7651/09b79/sentry-04.jpg 240w,
/static/394c63acdd46e24033c6e740decf7651/7cc5e/sentry-04.jpg 480w,
/static/394c63acdd46e24033c6e740decf7651/6a068/sentry-04.jpg 960w,
/static/394c63acdd46e24033c6e740decf7651/644c5/sentry-04.jpg 1440w,
/static/394c63acdd46e24033c6e740decf7651/0f98f/sentry-04.jpg 1920w,
/static/394c63acdd46e24033c6e740decf7651/cd367/sentry-04.jpg 2356w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;npm view &amp;lt;package name&gt; peerDependencies --json&lt;/code&gt; is the way to verify package dependencies without going to the npm website or “guessing”!&lt;/p&gt;
&lt;h2 id=&quot;finally-success-with-ng-serve&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#finally-success-with-ng-serve&quot; aria-label=&quot;finally success with ng serve permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Finally success with ng serve!&lt;/h2&gt;
&lt;p&gt;Finally, &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; worked! I ran &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; and the application finally started building.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a168d2227a6eab773f66522b9aa0a07a/ca222/npm-install-success.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcqgQD//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAXEAEAAwAAAAAAAAAAAAAAAAABABBB/9oACAEBAAE/IVTWLX//2gAMAwEAAgADAAAAEPPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRABAAIDAAAAAAAAAAAAAAAAAQCREBEh/9oACAEBAAE/EBN0MRXt4//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;npm install success&quot;
        title=&quot;npm install success&quot;
        src=&quot;/static/a168d2227a6eab773f66522b9aa0a07a/6a068/npm-install-success.jpg&quot;
        srcset=&quot;/static/a168d2227a6eab773f66522b9aa0a07a/09b79/npm-install-success.jpg 240w,
/static/a168d2227a6eab773f66522b9aa0a07a/7cc5e/npm-install-success.jpg 480w,
/static/a168d2227a6eab773f66522b9aa0a07a/6a068/npm-install-success.jpg 960w,
/static/a168d2227a6eab773f66522b9aa0a07a/644c5/npm-install-success.jpg 1440w,
/static/a168d2227a6eab773f66522b9aa0a07a/0f98f/npm-install-success.jpg 1920w,
/static/a168d2227a6eab773f66522b9aa0a07a/ca222/npm-install-success.jpg 1984w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, there was still a Sentry error preventing the app from starting:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7e81837b1114ac0fe889bad4af70f54a/8b3ab/angular-ns-sentry-error.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAealhVIf/8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEREP/aAAgBAQABBQIqKi7/AP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAAAABESBRYf/aAAgBAQABPyGUdEPSj//aAAwDAQACAAMAAAAQ4w//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgEFAAAAAAAAAAAAAAABABExECFBUaH/2gAIAQEAAT8QK8mgWsI8G4otzbr2f//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sentry error&quot;
        title=&quot;Sentry error&quot;
        src=&quot;/static/7e81837b1114ac0fe889bad4af70f54a/6a068/angular-ns-sentry-error.jpg&quot;
        srcset=&quot;/static/7e81837b1114ac0fe889bad4af70f54a/09b79/angular-ns-sentry-error.jpg 240w,
/static/7e81837b1114ac0fe889bad4af70f54a/7cc5e/angular-ns-sentry-error.jpg 480w,
/static/7e81837b1114ac0fe889bad4af70f54a/6a068/angular-ns-sentry-error.jpg 960w,
/static/7e81837b1114ac0fe889bad4af70f54a/644c5/angular-ns-sentry-error.jpg 1440w,
/static/7e81837b1114ac0fe889bad4af70f54a/0f98f/angular-ns-sentry-error.jpg 1920w,
/static/7e81837b1114ac0fe889bad4af70f54a/8b3ab/angular-ns-sentry-error.jpg 2390w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Since the issue was isolated to Sentry, I temporarily commented out the problematic code. The app compiled successfully!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/168e0d0d9916e0781dc21b501dfc3064/11a1a/angular-ns-success.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHJDMC//8QAFhAAAwAAAAAAAAAAAAAAAAAAABAR/9oACAEBAAEFAlD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAXEAEBAQEAAAAAAAAAAAAAAAARAAEh/9oACAEBAAE/IdxjrK//2gAMAwEAAgADAAAAEHw//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QGf/EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/EIf/xAAYEAADAQEAAAAAAAAAAAAAAAAAAREhcf/aAAgBAQABPxBOqzUqcj//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular success&quot;
        title=&quot;Angular success&quot;
        src=&quot;/static/168e0d0d9916e0781dc21b501dfc3064/6a068/angular-ns-success.jpg&quot;
        srcset=&quot;/static/168e0d0d9916e0781dc21b501dfc3064/09b79/angular-ns-success.jpg 240w,
/static/168e0d0d9916e0781dc21b501dfc3064/7cc5e/angular-ns-success.jpg 480w,
/static/168e0d0d9916e0781dc21b501dfc3064/6a068/angular-ns-success.jpg 960w,
/static/168e0d0d9916e0781dc21b501dfc3064/644c5/angular-ns-success.jpg 1440w,
/static/168e0d0d9916e0781dc21b501dfc3064/0f98f/angular-ns-success.jpg 1920w,
/static/168e0d0d9916e0781dc21b501dfc3064/11a1a/angular-ns-success.jpg 2382w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After the build succeeded, I updated the Sentry code to follow the latest version documentation. &lt;code class=&quot;language-text&quot;&gt;ng serve&lt;/code&gt; looked good, so I committed the changes and pushed to CI.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1e64cb543a93227ab1ff114079d8af42/af4e4/angular-ns-sentry-update.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIFAf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGYmqUicS//xAAbEAABBAMAAAAAAAAAAAAAAAABAAIDEhARMf/aAAgBAQABBQIuO7OUEpoe4//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABkQAAIDAQAAAAAAAAAAAAAAAAExACCRIf/aAAgBAQAGPwJnYzs6S6f/xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhMVH/2gAIAQEAAT8hpXoJcWFnufp0q9I20qLMp//aAAwDAQACAAMAAAAQ+P8A/8QAFREBAQAAAAAAAAAAAAAAAAAAAAH/2gAIAQMBAT8QSv/EABYRAQEBAAAAAAAAAAAAAAAAAAABIf/aAAgBAgEBPxDVf//EABwQAAICAgMAAAAAAAAAAAAAAAERACFR4UFhof/aAAgBAQABPxBRJAoGqE73MEj0shJDMrUJytw8wOtwS7CjP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Sentry code&quot;
        title=&quot;Angular Sentry code&quot;
        src=&quot;/static/1e64cb543a93227ab1ff114079d8af42/6a068/angular-ns-sentry-update.jpg&quot;
        srcset=&quot;/static/1e64cb543a93227ab1ff114079d8af42/09b79/angular-ns-sentry-update.jpg 240w,
/static/1e64cb543a93227ab1ff114079d8af42/7cc5e/angular-ns-sentry-update.jpg 480w,
/static/1e64cb543a93227ab1ff114079d8af42/6a068/angular-ns-sentry-update.jpg 960w,
/static/1e64cb543a93227ab1ff114079d8af42/644c5/angular-ns-sentry-update.jpg 1440w,
/static/1e64cb543a93227ab1ff114079d8af42/0f98f/angular-ns-sentry-update.jpg 1920w,
/static/1e64cb543a93227ab1ff114079d8af42/af4e4/angular-ns-sentry-update.jpg 2234w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;not-the-end---ng-build-failed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#not-the-end---ng-build-failed&quot; aria-label=&quot;not the end   ng build failed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Not the end - ng build failed&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b48f194a8fbf33c7c3c740c66ef9e888/0ae7a/angular-build-failed-ci.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.74999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHJoxZAB//EABUQAQEAAAAAAAAAAAAAAAAAACBB/9oACAEBAAEFAof/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAACAgMAAAAAAAAAAAAAAAAAARARMUGB/9oACAEBAAE/IdBwla4PMf/aAAwDAQACAAMAAAAQ6+//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAQQDAAAAAAAAAAAAAAABIQAQEbExQWH/2gAIAQEAAT8QcSHTuhMFgE9brkt//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Sentry code&quot;
        title=&quot;Angular Sentry code&quot;
        src=&quot;/static/b48f194a8fbf33c7c3c740c66ef9e888/6a068/angular-build-failed-ci.jpg&quot;
        srcset=&quot;/static/b48f194a8fbf33c7c3c740c66ef9e888/09b79/angular-build-failed-ci.jpg 240w,
/static/b48f194a8fbf33c7c3c740c66ef9e888/7cc5e/angular-build-failed-ci.jpg 480w,
/static/b48f194a8fbf33c7c3c740c66ef9e888/6a068/angular-build-failed-ci.jpg 960w,
/static/b48f194a8fbf33c7c3c740c66ef9e888/644c5/angular-build-failed-ci.jpg 1440w,
/static/b48f194a8fbf33c7c3c740c66ef9e888/0f98f/angular-build-failed-ci.jpg 1920w,
/static/b48f194a8fbf33c7c3c740c66ef9e888/0ae7a/angular-build-failed-ci.jpg 2034w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/79fc7808d6144483860015b0d7c14914/46ed1/angular-build-failed-code.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABkUgmhP/EABkQAAIDAQAAAAAAAAAAAAAAAAABAgQRIf/aAAgBAQABBQJk+UtZ/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGBABAAMBAAAAAAAAAAAAAAAAAAEDMYH/2gAIAQEABj8CU9bL/8QAGRABAQADAQAAAAAAAAAAAAAAAQARITHx/9oACAEBAAE/IY3AU3w3vX//2gAMAwEAAgADAAAAEPA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQARMUFRcdH/2gAIAQEAAT8QAJSYI5XmYOY6qe/s/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Sentry code&quot;
        title=&quot;Angular Sentry code&quot;
        src=&quot;/static/79fc7808d6144483860015b0d7c14914/6a068/angular-build-failed-code.jpg&quot;
        srcset=&quot;/static/79fc7808d6144483860015b0d7c14914/09b79/angular-build-failed-code.jpg 240w,
/static/79fc7808d6144483860015b0d7c14914/7cc5e/angular-build-failed-code.jpg 480w,
/static/79fc7808d6144483860015b0d7c14914/6a068/angular-build-failed-code.jpg 960w,
/static/79fc7808d6144483860015b0d7c14914/644c5/angular-build-failed-code.jpg 1440w,
/static/79fc7808d6144483860015b0d7c14914/0f98f/angular-build-failed-code.jpg 1920w,
/static/79fc7808d6144483860015b0d7c14914/46ed1/angular-build-failed-code.jpg 4899w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, when I pushed the changes to Netlify, the preview build failed with a pretty straightforward error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;55&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; $ npm run build
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;55&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; jira&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;clone&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;angular&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;frontend&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt; build
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;55&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; ng build &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;sourceMap&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;56&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Error&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Unknown argument&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; sourceMap
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;56&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ​
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;56&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PM&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;build.command&quot;&lt;/span&gt; failed &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see in my &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;, the &lt;code class=&quot;language-text&quot;&gt;build&lt;/code&gt; command was mapped to &lt;code class=&quot;language-text&quot;&gt;ng build --sourceMap=true&lt;/code&gt;, and I believe this option has been removed in later Angular versions. I removed the &lt;code class=&quot;language-text&quot;&gt;--sourceMap=true&lt;/code&gt; flag and let the build run through. Since it worked locally, I didn’t expect any surprises when pushing to Netlify - and indeed, it was successful! With that, our journey to migrate to Angular 14 is complete. 🎉&lt;/p&gt;
&lt;p&gt;👉 You can check out the full changes in this PR: &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/106&quot;&gt;https://github.com/trungvose/jira-clone-angular/pull/106&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/260c1c9dde2e6b19fed7612c8e1fee4b/df51d/angular-build-failed-success.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQACBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeXhAiP/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAACAgMAAAAAAAAAAAAAAAAAARARITFB/9oACAEBAAE/IbY9xzCKj//aAAwDAQACAAMAAAAQYM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgMAAwAAAAAAAAAAAAABABEhMUEQYZH/2gAIAQEAAT8QxGCFU0fZfojfByI27q9+P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Sentry code&quot;
        title=&quot;Angular Sentry code&quot;
        src=&quot;/static/260c1c9dde2e6b19fed7612c8e1fee4b/6a068/angular-build-failed-success.jpg&quot;
        srcset=&quot;/static/260c1c9dde2e6b19fed7612c8e1fee4b/09b79/angular-build-failed-success.jpg 240w,
/static/260c1c9dde2e6b19fed7612c8e1fee4b/7cc5e/angular-build-failed-success.jpg 480w,
/static/260c1c9dde2e6b19fed7612c8e1fee4b/6a068/angular-build-failed-success.jpg 960w,
/static/260c1c9dde2e6b19fed7612c8e1fee4b/644c5/angular-build-failed-success.jpg 1440w,
/static/260c1c9dde2e6b19fed7612c8e1fee4b/0f98f/angular-build-failed-success.jpg 1920w,
/static/260c1c9dde2e6b19fed7612c8e1fee4b/df51d/angular-build-failed-success.jpg 2400w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;-angular-14-is-live-on-prod&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-angular-14-is-live-on-prod&quot; aria-label=&quot; angular 14 is live on prod permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ Angular 14 is live on PROD!&lt;/h2&gt;
&lt;p&gt;Merged and deployed successfully. Everything looks good.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f2d9bb16243b138a723b50d6817bc4bd/2c89e/angular-14-deployed.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe0loZR//8QAFhAAAwAAAAAAAAAAAAAAAAAAEBEg/9oACAEBAAEFAgo//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAACAx/9oACAEBAAY/AiL/AP/EABkQAQADAQEAAAAAAAAAAAAAAAEAETEQUf/aAAgBAQABPyGgcuAMU8hnf//aAAwDAQACAAMAAAAQoA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAACAQUAAAAAAAAAAAAAAAABERAAITGRsf/aAAgBAQABPxBysyq2ENRu0//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 14 is live on PROD!&quot;
        title=&quot;Angular 14 is live on PROD!&quot;
        src=&quot;/static/f2d9bb16243b138a723b50d6817bc4bd/6a068/angular-14-deployed.jpg&quot;
        srcset=&quot;/static/f2d9bb16243b138a723b50d6817bc4bd/09b79/angular-14-deployed.jpg 240w,
/static/f2d9bb16243b138a723b50d6817bc4bd/7cc5e/angular-14-deployed.jpg 480w,
/static/f2d9bb16243b138a723b50d6817bc4bd/6a068/angular-14-deployed.jpg 960w,
/static/f2d9bb16243b138a723b50d6817bc4bd/644c5/angular-14-deployed.jpg 1440w,
/static/f2d9bb16243b138a723b50d6817bc4bd/2c89e/angular-14-deployed.jpg 1568w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#whats-next&quot; aria-label=&quot;whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What’s Next?&lt;/h2&gt;
&lt;p&gt;This was just the first step - upgrading to Angular 14. The journey continues with upgrades to versions 15, 16, 17, 18, 19, and finally 20. Each step will likely bring its own challenges and dependencies to resolve.&lt;/p&gt;
&lt;p&gt;The key takeaways from this Angular 14 upgrade:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s okay to use &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt; when dealing with peer dependency conflicts during major version upgrades, but &lt;strong&gt;make sure to resolve all dependencies before merging to the main branch&lt;/strong&gt;. Otherwise, CI builds and other developers’ machines will fail to run &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; and they’ll also need to use &lt;code class=&quot;language-text&quot;&gt;--force&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check package version using &lt;code class=&quot;language-text&quot;&gt;npm view &amp;lt;package&gt; versions --json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check package compatibility using &lt;code class=&quot;language-text&quot;&gt;npm view &amp;lt;package&gt; peerDependencies --json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Upgrade dependencies incrementally and test after each step&lt;/li&gt;
&lt;li&gt;Don’t be afraid to temporarily comment out problematic code to isolate issues&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stay tuned for the next part where we upgrade Angular 15!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Typescript this and implement debounce]]></title><link>https://trungvose.comtypescript-this-debounce/</link><guid isPermaLink="false">https://trungvose.comtypescript-this-debounce/</guid><pubDate>Sat, 07 Jun 2025 07:45:00 GMT</pubDate><content:encoded>&lt;p&gt;Back in 2020, I was in an interview and the interviewer asked me to implement a debounce function. I was surprised — I had been using debounce functions for years, mostly from libraries like Lodash, but I never actually had to write one from scratch. I did not solve it back then. Recently I stumbled upon the same question again, and this time I decided to finally sit down and do it.&lt;/p&gt;
&lt;p&gt;Let go through it together — what debounce is, why it matters, and how to implement one correctly in TypeScript.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-debounce&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-debounce&quot; aria-label=&quot;what is debounce permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is debounce?&lt;/h2&gt;
&lt;p&gt;The idea of debouncing is to control how frequently a function runs over a period of time. You give it a time window, and it ensures the function only gets called once after that window has passed without further calls.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sayHello &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hello&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Only logs once after 300ms&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Debouncing makes sure &lt;code class=&quot;language-text&quot;&gt;sayHello()&lt;/code&gt; only runs after a set delay (&lt;code class=&quot;language-text&quot;&gt;300ms&lt;/code&gt;) has passed since the last time it was called.&lt;/p&gt;
&lt;p&gt;This is useful for scenarios like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;mousemove&lt;/code&gt; events: These can fire hundreds of times per second. You do not want to handle every single one.&lt;/li&gt;
&lt;li&gt;Search boxes: Instead of sending a request on every keystroke, you wait until the user stops typing for a short moment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example for &lt;code class=&quot;language-text&quot;&gt;mousemove&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;mousemove&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MouseEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Mouse at (&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientX&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientY&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the log only shows up at most once every 100ms, no matter how fast you move the mouse.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-1-basic-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-basic-implementation&quot; aria-label=&quot;step 1 basic implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: Basic implementation&lt;/h2&gt;
&lt;p&gt;Let start with the function signature:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;func&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wait&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It takes two inputs: a function to debounce and a wait time in milliseconds. It returns a new function that wraps the original.&lt;/p&gt;
&lt;p&gt;A basic version looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;func&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wait&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; timer&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounceFn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    timer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wait&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; debounceFn&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;How this works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first time you call &lt;code class=&quot;language-text&quot;&gt;debounceFn&lt;/code&gt;, it sets a timer.&lt;/li&gt;
&lt;li&gt;If you call it again before the timer finishes, it clears the old timer and starts a new one.&lt;/li&gt;
&lt;li&gt;The original &lt;code class=&quot;language-text&quot;&gt;func&lt;/code&gt; only gets executed once the calls stop for &lt;code class=&quot;language-text&quot;&gt;wait&lt;/code&gt; milliseconds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Test case:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sayHello &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hello&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Sets a timer&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Resets the timer after 200ms&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Result: &apos;hello&apos; logs after 500ms total&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-2-this-test-failed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-this-test-failed&quot; aria-label=&quot;step 2 this test failed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; test failed&lt;/h2&gt;
&lt;p&gt;Life is not that easy, one test failed 😂&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;callbacks can access `this`&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;done&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; increment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; delta&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; delta&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    val&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    increment&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// still 2, not yet updated&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// expect 2 + 3&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But the result was wrong. The value stayed at 2.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; inside the original function was lost.&lt;/li&gt;
&lt;li&gt;Arguments like &lt;code class=&quot;language-text&quot;&gt;delta&lt;/code&gt; were not passed through at all.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In our earlier version:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// no context, no arguments&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wait&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That &lt;code class=&quot;language-text&quot;&gt;func()&lt;/code&gt; call is missing both &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; and the original parameters. Time to fix both.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-3-fix-this-and-arguments-using-apply&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-fix-this-and-arguments-using-apply&quot; aria-label=&quot;step 3 fix this and arguments using apply permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3: Fix &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; and arguments using &lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To preserve &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; and the function parameters, I updated the debounce to use &lt;code class=&quot;language-text&quot;&gt;.apply&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply&quot;&gt;apply&lt;/a&gt; lets you call a function with a specific &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; value and pass arguments as an array.&lt;/p&gt;
&lt;p&gt;Also, TypeScript needs a little help to understand the use of &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; inside the function. You can explicitly annotate it as the first argument like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a TypeScript-specific feature that was introduced in version 2.0 — more details &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#specifying-the-type-of-this-for-functions&quot;&gt;Specifying the type of this for functions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So now the code looks like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;func&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wait&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; timer&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounceFn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    timer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// pass this and args correctly&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wait&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-result&quot; aria-label=&quot;final result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final Result&lt;/h2&gt;
&lt;p&gt;Now we have a debounce that works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It delays execution properly&lt;/li&gt;
&lt;li&gt;It preserves &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; context&lt;/li&gt;
&lt;li&gt;It forwards all arguments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This version passed all my tests.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; increment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; delta&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; delta&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  val&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  increment&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After 10ms, obj.val === 5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That was a fun exercise for sure! You can practice it here at &lt;a href=&quot;https://www.greatfrontend.com/interviews/study/gfe75/questions/javascript/debounce&quot;&gt;greatfrontend.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f117e009aab21118565650f89cfbf28a/83227/typescript-this.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAYCBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeWsdEYsh//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAICAwAAAAAAAAAAAAAAAAABEWEgIVH/2gAIAQEAAT8h3xkUyKeH/9oADAMBAAIAAwAAABC47//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EKr/xAAaEAADAQADAAAAAAAAAAAAAAAAARHhIVGx/9oACAEBAAE/EHU8S8C8BNXCLofDP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;typescript this&quot;
        title=&quot;typescript this&quot;
        src=&quot;/static/f117e009aab21118565650f89cfbf28a/6a068/typescript-this.jpg&quot;
        srcset=&quot;/static/f117e009aab21118565650f89cfbf28a/09b79/typescript-this.jpg 240w,
/static/f117e009aab21118565650f89cfbf28a/7cc5e/typescript-this.jpg 480w,
/static/f117e009aab21118565650f89cfbf28a/6a068/typescript-this.jpg 960w,
/static/f117e009aab21118565650f89cfbf28a/644c5/typescript-this.jpg 1440w,
/static/f117e009aab21118565650f89cfbf28a/0f98f/typescript-this.jpg 1920w,
/static/f117e009aab21118565650f89cfbf28a/83227/typescript-this.jpg 2505w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Classnames implementation in TypeScript]]></title><link>https://trungvose.comclassname-naive-implementation/</link><guid isPermaLink="false">https://trungvose.comclassname-naive-implementation/</guid><pubDate>Sat, 31 May 2025 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Still the same conversation with my friend who recently went for an interview. After the Python indentation task, the next challenge was another familiar one for front end engineers — implement the &lt;code class=&quot;language-text&quot;&gt;classnames&lt;/code&gt; utility, or the newer &lt;code class=&quot;language-text&quot;&gt;clsx&lt;/code&gt; used in React apps.&lt;/p&gt;
&lt;p&gt;If you have used React, you have probably written code like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;btn&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isActive &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;active&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This utility helps combine class names conditionally and neatly, now let’s see how simple it is to implement it.&lt;/p&gt;
&lt;h2 id=&quot;requirement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requirement&quot; aria-label=&quot;requirement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Requirement&lt;/h2&gt;
&lt;p&gt;classnames is a commonly-used utility in modern front end applications to conditionally join CSS class names together. If you’ve written React applications, you likely have used a similar library.&lt;/p&gt;
&lt;p&gt;Implement the classnames function.&lt;/p&gt;
&lt;p&gt;Examples&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;foo bar&apos;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;foo bar&apos;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&apos;foo-bar&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;foo-bar&apos;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&apos;foo-bar&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;&apos;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;foo bar&apos;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;foo bar&apos;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; qux&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;foo qux&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Arrays will be recursively flattened as per the rules above.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; d&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;a b c&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Values can be mixed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    bar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    duck&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;baz&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; quux&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;foo bar baz quux&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Falsey values are ignored.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; baz&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;bar&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In addition, the returned string should not have any leading or trailing whitespace.&lt;/p&gt;
&lt;h2 id=&quot;thought-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#thought-process&quot; aria-label=&quot;thought process permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Thought process&lt;/h2&gt;
&lt;p&gt;I have solved similar problems before, like converting &lt;code class=&quot;language-text&quot;&gt;snake_case&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;camelCase&lt;/code&gt;, which taught me to check the type first — if the value is a string, object, or array — and then deal with each case properly.&lt;/p&gt;
&lt;p&gt;This problem is about combining class names based on rules. The tricky part is dealing with mixed types, recursive arrays, and ignoring falsey values like &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;&apos;&apos;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I broke the problem into smaller pieces:&lt;/p&gt;
&lt;h3 id=&quot;1-if-the-input-is-a-string&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-if-the-input-is-a-string&quot; aria-label=&quot;1 if the input is a string permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. If the input is a string&lt;/h3&gt;
&lt;p&gt;Use the string as-is (after trimming).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;string&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-if-the-input-is-a-number&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-if-the-input-is-a-number&quot; aria-label=&quot;2 if the input is a number permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. If the input is a number&lt;/h3&gt;
&lt;p&gt;Just convert it to string.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;number&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-if-the-input-is-an-array&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-if-the-input-is-an-array&quot; aria-label=&quot;3 if the input is an array permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. If the input is an array&lt;/h3&gt;
&lt;p&gt;Recursively flatten each value inside the array.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;flattenClass&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;4-if-the-input-is-an-object&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-if-the-input-is-an-object&quot; aria-label=&quot;4 if the input is an object permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. If the input is an object&lt;/h3&gt;
&lt;p&gt;Pick only the keys that have truthy values.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;object&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ignore everything else (&lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;, empty string). After applying all these steps, collect the results and join with a space.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-implementation&quot; aria-label=&quot;final implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final implementation&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClassValue&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ClassDictionary
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ClassArray&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClassDictionary&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClassArray&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ClassValue&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;classNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ClassValue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;flattenClass&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Boolean&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;flattenClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ClassValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;string&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;number&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;flattenClass&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;object&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;results&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#results&quot; aria-label=&quot;results permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Results&lt;/h2&gt;
&lt;p&gt;You can practice this question &lt;a href=&quot;https://www.greatfrontend.com/interviews/study/gfe75/questions/javascript/classnames&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b42464e683d029e47c352a76fd979b7/60408/classnames.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABxyFQf//EABcQAQADAAAAAAAAAAAAAAAAAAABESD/2gAIAQEAAQUCQvH/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAADAQEAAAAAAAAAAAAAAAAAAUERUf/aAAgBAQABPyGIxR8NkRBH/9oADAMBAAIAAwAAABDID//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAwEBAAAAAAAAAAAAAAEAIRExYVGx/9oACAEBAAE/EMCyuceREeqqMNQ4pBdjqP1BU//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;classnames&quot;
        title=&quot;classnames&quot;
        src=&quot;/static/9b42464e683d029e47c352a76fd979b7/6a068/classnames.jpg&quot;
        srcset=&quot;/static/9b42464e683d029e47c352a76fd979b7/09b79/classnames.jpg 240w,
/static/9b42464e683d029e47c352a76fd979b7/7cc5e/classnames.jpg 480w,
/static/9b42464e683d029e47c352a76fd979b7/6a068/classnames.jpg 960w,
/static/9b42464e683d029e47c352a76fd979b7/644c5/classnames.jpg 1440w,
/static/9b42464e683d029e47c352a76fd979b7/0f98f/classnames.jpg 1920w,
/static/9b42464e683d029e47c352a76fd979b7/60408/classnames.jpg 5092w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Validating Python Indentation in TypeScript]]></title><link>https://trungvose.comvalidating-python-indentation/</link><guid isPermaLink="false">https://trungvose.comvalidating-python-indentation/</guid><pubDate>Sun, 18 May 2025 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A friend of mine recently went for a coding interview and was given a small but tricky challenge: validate if a block of Python code has the correct indentation.&lt;/p&gt;
&lt;p&gt;The catch? You cannot run the code. You are not supposed to validate syntax either. You just need to tell if the indentation structure is valid.&lt;/p&gt;
&lt;p&gt;We were chatting and I thought — actually, this is a nice little puzzle. So I decided to write a validator in TypeScript just for fun.&lt;/p&gt;
&lt;h2 id=&quot;scope&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scope&quot; aria-label=&quot;scope permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scope&lt;/h2&gt;
&lt;p&gt;This is a structural check only. We do &lt;strong&gt;not&lt;/strong&gt; validate Python syntax or runtime behaviour. Just indentation.&lt;/p&gt;
&lt;h2 id=&quot;assumptions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#assumptions&quot; aria-label=&quot;assumptions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Assumptions&lt;/h2&gt;
&lt;p&gt;To keep things simple and deterministic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indentation uses &lt;strong&gt;4 spaces only&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;No mixing tabs and spaces&lt;/li&gt;
&lt;li&gt;Any line ending with &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; should be followed by a more indented line&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example&quot; aria-label=&quot;example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example&lt;/h2&gt;
&lt;h3 id=&quot;valid-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#valid-code&quot; aria-label=&quot;valid code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Valid Code&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Positive number&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Even number&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Odd number&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;invalid-code-examples&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#invalid-code-examples&quot; aria-label=&quot;invalid code examples permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Invalid Code Examples&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Missing indentation after &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Positive number&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# not indented&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Inconsistent indentation&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Positive number&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This line has wrong indentation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 2 spaces instead of 4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Invalid dedent&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Positive number&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Too much indentation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 8 spaces when only 4 is valid&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Missing indentation in nested block&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Missing indentation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# should be indented with 8 spaces&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation&quot; aria-label=&quot;implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation&lt;/h2&gt;
&lt;h3 id=&quot;ideas&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ideas&quot; aria-label=&quot;ideas permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ideas&lt;/h3&gt;
&lt;p&gt;When reading the requirement, my thought process was pretty straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Split&lt;/strong&gt; the code by newlines&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skip&lt;/strong&gt; empty lines or lines with only comments&lt;/li&gt;
&lt;li&gt;For each non-empty line:
&lt;ul&gt;
&lt;li&gt;Count the number of &lt;strong&gt;leading spaces&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Compare with the &lt;strong&gt;current expected indent&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;If the line ends with a colon (&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt;), expect an indent increase on the next line. This is different from the valid parenthesis problem where it’s more straightforward - if the string starts with an opening bracket (&lt;code class=&quot;language-text&quot;&gt;{&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;[&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;(&lt;/code&gt;), you know exactly what the expected closing bracket (&lt;code class=&quot;language-text&quot;&gt;}&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;]&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;)&lt;/code&gt;) should be&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;stack to track indent levels&lt;/strong&gt; - push new levels when we enter a block, pop when we leave&lt;/li&gt;
&lt;li&gt;If the indent does not match any level in the stack, it is invalid&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;step-1-basic-structure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-basic-structure&quot; aria-label=&quot;step 1 basic structure permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step #1: Basic Structure&lt;/h3&gt;
&lt;p&gt;Let’s start with the basic structure:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TAB_SIZE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validatePythonIndentation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; indents&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;\n&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rawLine &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; line &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rawLine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trimEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// DO THE REAL STUFF&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;countSpacesFromBeginning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trimStart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isLineStartNewBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;:&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;step-2-handling-indentation-nesting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-handling-indentation-nesting&quot; aria-label=&quot;step 2 handling indentation nesting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step #2: Handling Indentation Nesting&lt;/h3&gt;
&lt;p&gt;Now we add the logic to handle indentation nesting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check the current line’s indentation against the expected levels stored in a &lt;strong&gt;stack&lt;/strong&gt; (&lt;code class=&quot;language-text&quot;&gt;indents&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;If the current indentation is &lt;strong&gt;less&lt;/strong&gt; than the top of the stack, it means we are &lt;strong&gt;leaving&lt;/strong&gt; one or more blocks — so we pop those indentation levels until the current indent matches the new top (I know it is yet clicked, the see the example below)&lt;/li&gt;
&lt;li&gt;If the current indentation does &lt;strong&gt;not match&lt;/strong&gt; the new top after popping, it is invalid&lt;/li&gt;
&lt;li&gt;If the line ends with &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt;, push a new expected indentation (current + 4 spaces) to the stack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tricky part is the &lt;strong&gt;pop loop&lt;/strong&gt; that handles dedents:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;const TAB_SIZE = 4

export function validatePythonIndentation(input: string): boolean {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; if (!input) {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   return false;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; }
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  // Start with [0] because the first line should have no indentation
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  // This represents the base level of indentation (0 spaces)  
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  const indents: number[] = [0];
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; const lines = input.split(&apos;\n&apos;);
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; for (const rawLine of lines) {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   const line = rawLine.trimEnd();
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   if (line === &apos;&apos;) {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     continue // skip empty line;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   }
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   const currentIndent = countSpacesFromBeginning(line);
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    while (indents.length &amp;amp;&amp;amp; currentIndent &amp;lt; indents[indents.length - 1]) {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      indents.pop();
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    }
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   const expectedIndent = indents[indents.length - 1];
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   if (currentIndent !== expectedIndent) {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     return false;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   }
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   if (isLineStartNewBlock(line)) {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     const nextExpectedIndent = currentIndent + TAB_SIZE;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     indents.push(nextExpectedIndent);
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   }
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; }
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  return true;
&lt;/span&gt;}

function countSpacesFromBeginning(line: string): number {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return line.length - line.trimStart().length;
&lt;/span&gt;}

function isLineStartNewBlock(line: string): boolean {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return line.endsWith(&apos;:&apos;);
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What is going on here?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The stack stores allowed indentation levels, starting with &lt;code class=&quot;language-text&quot;&gt;[0]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;When a line has less indent than the last recorded indent, it means we must close some blocks&lt;/li&gt;
&lt;li&gt;The loop pops those bigger indents until the top matches or is less than or equal to the current indent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why return true&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;Our validator checks every line. If no line violates the expected indent, the input is valid.&lt;/p&gt;
&lt;p&gt;So return true at the end means:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I saw no mistake, so I approve.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;how-it-works-a-concrete-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-it-works-a-concrete-example&quot; aria-label=&quot;how it works a concrete example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How It Works: A Concrete Example&lt;/h3&gt;
&lt;p&gt;Let’s walk through this Python snippet using our indentation validator:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Even&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Odd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;done&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We assume &lt;code class=&quot;language-text&quot;&gt;TAB_SIZE = 4&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;initial-setup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#initial-setup&quot; aria-label=&quot;initial setup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Initial Setup&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0]&lt;/code&gt;
Starts with &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; to mean top-level code should have no indentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;line-by-line&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#line-by-line&quot; aria-label=&quot;line by line permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Line-by-line&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;number = 5&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent = 0&lt;/li&gt;
&lt;li&gt;Top of stack = 0 → valid&lt;/li&gt;
&lt;li&gt;Does not end with &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; → no new block&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0]&lt;/code&gt; (unchanged)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;if number &gt; 0:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent = 0&lt;/li&gt;
&lt;li&gt;Top of stack = 0 → valid&lt;/li&gt;
&lt;li&gt;Ends with &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; → push &lt;code class=&quot;language-text&quot;&gt;0 + 4 = 4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0, 4]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;if number % 2 == 0:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent = 4&lt;/li&gt;
&lt;li&gt;Top of stack = 4 → valid&lt;/li&gt;
&lt;li&gt;Ends with &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; → push &lt;code class=&quot;language-text&quot;&gt;4 + 4 = 8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0, 4, 8]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;print(&quot;Even&quot;)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent = 8&lt;/li&gt;
&lt;li&gt;Top of stack = 8 → valid&lt;/li&gt;
&lt;li&gt;No &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; → no push&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0, 4, 8]&lt;/code&gt; (unchanged)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;else:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent = 4&lt;/li&gt;
&lt;li&gt;Top of stack = 8 → mismatch, but less than top
→ Pop until top is &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;After pop: &lt;code class=&quot;language-text&quot;&gt;indents = [0, 4]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Top = 4 → match&lt;/li&gt;
&lt;li&gt;Ends with &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; → push &lt;code class=&quot;language-text&quot;&gt;4 + 4 = 8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0, 4, 8]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;print(&quot;Odd&quot;)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent = 8&lt;/li&gt;
&lt;li&gt;Top = 8 → match&lt;/li&gt;
&lt;li&gt;No &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; → no push&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0, 4, 8]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;print(&quot;done&quot;)&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indent = 0&lt;/li&gt;
&lt;li&gt;Top = 8 → pop to 4 → pop to 0&lt;/li&gt;
&lt;li&gt;Top = 0 → match&lt;/li&gt;
&lt;li&gt;No &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; → done&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;indents = [0]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;no-invalid-indentation-found&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#no-invalid-indentation-found&quot; aria-label=&quot;no invalid indentation found permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;No invalid indentation found&lt;/h4&gt;
&lt;p&gt;So at the end, we never returned &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;. That means the indentation is valid, so we return &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;playground&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#playground&quot; aria-label=&quot;playground permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Playground&lt;/h2&gt;
&lt;p&gt;Try it out in the &lt;a href=&quot;https://stackblitz.com/edit/python-identation?file=src%2Fvalidate-python-indentation.ts&quot;&gt;TypeScript Playground&lt;/a&gt;.&lt;/p&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/python-identation?ctl=1&amp;embed=1&amp;file=src%2Fvalidate-python-indentation.ts&quot;&gt;&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[React Aria Components - Slider with filled background]]></title><link>https://trungvose.comreact-aria-component-slider-filled-background/</link><guid isPermaLink="false">https://trungvose.comreact-aria-component-slider-filled-background/</guid><pubDate>Wed, 30 Apr 2025 01:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Even though I’ve been an Engineering Manager for the past two years, I still love getting my hands dirty with code! Recently, I helped my team with a cool UI challenge - customizing React Aria’s Slider component to show a filled background between two thumbs.&lt;/p&gt;
&lt;h2 id=&quot;before--after&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#before--after&quot; aria-label=&quot;before  after permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Before &amp;#x26; After&lt;/h2&gt;
&lt;p&gt;Let’s see how the slider looks with and without the fill effect:&lt;/p&gt;
&lt;h3 id=&quot;slider-without-filled-background&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slider-without-filled-background&quot; aria-label=&quot;slider without filled background permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Slider without filled background&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f2ae8c85a15e3d1370f0a5d18c2cde6d/07112/basic-slider.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAHRSV2lHDf/xAAZEAEAAgMAAAAAAAAAAAAAAAABAAIQERL/2gAIAQEAAQUCNgdRJXP/xAAXEQADAQAAAAAAAAAAAAAAAAAAARES/9oACAEDAQE/AYoYR//EABYRAQEBAAAAAAAAAAAAAAAAAAEAAv/aAAgBAgEBPwFY1f/EABwQAAIABwAAAAAAAAAAAAAAAAARAyAhMTNhof/aAAgBAQAGPwKkJ7MXCyk//8QAGhAAAwADAQAAAAAAAAAAAAAAAAERITFRgf/aAAgBAQABPyGJNItHkZ9z6Lx55oZ9Ym+iP//aAAwDAQACAAMAAAAQH+//xAAWEQEBAQAAAAAAAAAAAAAAAAABEQD/2gAIAQMBAT8QoAJqb//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEWH/2gAIAQIBAT8Q4yBl/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITGRUf/aAAgBAQABPxAj2KLVk3mQwpwMXTWNEqvYdoo+XKHXs0bs/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Default Slider&quot;
        title=&quot;Default Slider&quot;
        src=&quot;/static/f2ae8c85a15e3d1370f0a5d18c2cde6d/6a068/basic-slider.jpg&quot;
        srcset=&quot;/static/f2ae8c85a15e3d1370f0a5d18c2cde6d/09b79/basic-slider.jpg 240w,
/static/f2ae8c85a15e3d1370f0a5d18c2cde6d/7cc5e/basic-slider.jpg 480w,
/static/f2ae8c85a15e3d1370f0a5d18c2cde6d/6a068/basic-slider.jpg 960w,
/static/f2ae8c85a15e3d1370f0a5d18c2cde6d/644c5/basic-slider.jpg 1440w,
/static/f2ae8c85a15e3d1370f0a5d18c2cde6d/07112/basic-slider.jpg 1880w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;em&gt;Basic slider without fill effect - just the track and thumb&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;slider-with-filled-background&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slider-with-filled-background&quot; aria-label=&quot;slider with filled background permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Slider with filled background&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5ed560772b728dc66c5e8888d69b368f/07112/filled-slider.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAHRSV2lHDf/xAAZEAEAAgMAAAAAAAAAAAAAAAABAAIQERL/2gAIAQEAAQUCNgdRJXP/xAAXEQADAQAAAAAAAAAAAAAAAAAAARES/9oACAEDAQE/AYpDCP/EABYRAQEBAAAAAAAAAAAAAAAAAAEAAv/aAAgBAgEBPwFY1f/EABwQAAIABwAAAAAAAAAAAAAAAAARAyAhMTNhof/aAAgBAQAGPwKkJ7MXCyk//8QAGxABAAIDAQEAAAAAAAAAAAAAAQARITFRQYH/2gAIAQEAAT8hpK8TRzE91+pfT56iesF7Cf/aAAwDAQACAAMAAAAQ3+//xAAWEQEBAQAAAAAAAAAAAAAAAAABEQD/2gAIAQMBAT8QsATU3//EABcRAQEBAQAAAAAAAAAAAAAAAAEAEWH/2gAIAQIBAT8Q4yBl/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITGRUf/aAAgBAQABPxAjgXFvRvMlZXgaesdJY6Ilew7RR8uUOvZo3Z//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Custom Slider&quot;
        title=&quot;Custom Slider&quot;
        src=&quot;/static/5ed560772b728dc66c5e8888d69b368f/6a068/filled-slider.jpg&quot;
        srcset=&quot;/static/5ed560772b728dc66c5e8888d69b368f/09b79/filled-slider.jpg 240w,
/static/5ed560772b728dc66c5e8888d69b368f/7cc5e/filled-slider.jpg 480w,
/static/5ed560772b728dc66c5e8888d69b368f/6a068/filled-slider.jpg 960w,
/static/5ed560772b728dc66c5e8888d69b368f/644c5/filled-slider.jpg 1440w,
/static/5ed560772b728dc66c5e8888d69b368f/07112/filled-slider.jpg 1880w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;em&gt;Enhanced slider with fill effect - notice the white background&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code--demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code--demo&quot; aria-label=&quot;source code  demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source Code &amp;#x26; Demo&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/react-aria-component-slider-filled-background?embed=1&amp;file=src%2Ftwo-thumbs-slider.tsx&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;the-challenge&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-challenge&quot; aria-label=&quot;the challenge permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Challenge&lt;/h2&gt;
&lt;p&gt;React Aria’s Slider component is awesome for accessibility, but it doesn’t come with a built-in way to show the filled portion of the slider. When you have a range slider (two thumbs), you might want to visually indicate the selected range with a filled background.&lt;/p&gt;
&lt;h2 id=&quot;the-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution&quot; aria-label=&quot;the solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Solution&lt;/h2&gt;
&lt;h3 id=&quot;one-thumb-slider&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#one-thumb-slider&quot; aria-label=&quot;one thumb slider permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;One thumb slider&lt;/h3&gt;
&lt;p&gt;First, let’s look at a basic single-thumb slider:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Label&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Slider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SliderOutput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SliderThumb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SliderTrack&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-aria-components&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slider&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Basic Slider&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderOutput&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderTrack&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderThumb&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderTrack&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slider&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important&lt;/strong&gt;: The above code will only render a label and a circle without any styling. You’ll need to add CSS to make it look good.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Since our project uses Tailwind CSS, here’s our styled version:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Label&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Slider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  SliderOutput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  SliderThumb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  SliderTrack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-aria-components&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OneThumbSlider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bg-linear-to-r from-purple-600 to-pink-600 p-12 rounded-lg flex justify-center&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slider&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;w-[250px]&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flex text-white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flex-1 text-left&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Basic Slider&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Label&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderOutput&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderTrack&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;relative w-full h-7&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;absolute h-2 top-[50%] translate-y-[-50%] w-full rounded-full bg-white/40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderThumb&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;h-7 w-7 top-[50%] rounded-full border border-solid border-purple-800/75 bg-white transition dragging:bg-purple-100 outline-hidden focus-visible:ring-2 ring-black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SliderTrack&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slider&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here’s how it looks:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e6de8140fc8766bad8a49fceca407a83/0a374/one-thumb-slider.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBP/EABUBAQEAAAAAAAAAAAAAAAAAAAMC/9oADAMBAAIQAxAAAAHF7M0nQOP/xAAZEAADAQEBAAAAAAAAAAAAAAAAARECEiL/2gAIAQEAAQUCsfbMesIiEf/EABkRAQACAwAAAAAAAAAAAAAAAAEAAhESQf/aAAgBAwEBPwGupyNBcz//xAAXEQADAQAAAAAAAAAAAAAAAAAAAQIS/9oACAECAQE/AXaNH//EABoQAAICAwAAAAAAAAAAAAAAAAABEBEhMUH/2gAIAQEABj8CrBwTjSj/xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhMVGBkeH/2gAIAQEAAT8htSrg2R5/MuhtmbkHqHzoAKCif//aAAwDAQACAAMAAAAQxx//xAAXEQEBAQEAAAAAAAAAAAAAAAABABEh/9oACAEDAQE/EMgIZgObf//EABURAQEAAAAAAAAAAAAAAAAAABEQ/9oACAECAQE/EDBP/8QAHBABAAIDAAMAAAAAAAAAAAAAAQARITFBUWGR/9oACAEBAAE/EEtx2B0NsDgq80gpehuiusB6mtDFGfjgEAaAoJ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;One Thumb Slider&quot;
        title=&quot;One Thumb Slider&quot;
        src=&quot;/static/e6de8140fc8766bad8a49fceca407a83/6a068/one-thumb-slider.jpg&quot;
        srcset=&quot;/static/e6de8140fc8766bad8a49fceca407a83/09b79/one-thumb-slider.jpg 240w,
/static/e6de8140fc8766bad8a49fceca407a83/7cc5e/one-thumb-slider.jpg 480w,
/static/e6de8140fc8766bad8a49fceca407a83/6a068/one-thumb-slider.jpg 960w,
/static/e6de8140fc8766bad8a49fceca407a83/644c5/one-thumb-slider.jpg 1440w,
/static/e6de8140fc8766bad8a49fceca407a83/0f98f/one-thumb-slider.jpg 1920w,
/static/e6de8140fc8766bad8a49fceca407a83/0a374/one-thumb-slider.jpg 2166w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This implementation is based on the &lt;a href=&quot;https://react-spectrum.adobe.com/react-aria/examples/opacity-slider.html&quot;&gt;React Aria Opacity Slider example&lt;/a&gt; from Adobe’s React Spectrum documentation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, let’s add the filled background. We’ll create a new prop to toggle this feature:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; interface OneThumbSliderProps {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   fill: boolean;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; }
&lt;/span&gt;
export function OneThumbSlider({ fill }: OneThumbSliderProps) {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return (
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &amp;lt;div className=&quot;bg-linear-to-r from-purple-600 to-pink-600 p-12 rounded-lg flex justify-center&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &amp;lt;Slider defaultValue={30} className=&quot;w-[250px]&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;div className=&quot;flex text-white&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         &amp;lt;Label className=&quot;flex-1 text-left&quot;&gt;Basic Slider&amp;lt;/Label&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         &amp;lt;SliderOutput /&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;/div&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;SliderTrack className=&quot;relative w-full h-7&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         {({ state }) =&gt; (
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           &amp;lt;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             &amp;lt;div className=&quot;absolute h-2 top-[50%] translate-y-[-50%] w-full rounded-full bg-white/40&quot; /&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;              {fill &amp;amp;&amp;amp; (
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                &amp;lt;div
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                  className=&quot;absolute h-2 top-[50%] translate-y-[-50%] rounded-full bg-white&quot;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                  style={{ width: state.getThumbPercent(0) * 100 + &apos;%&apos; }}
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                /&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;              )}
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             &amp;lt;SliderThumb className=&quot;h-7 w-7 top-[50%] rounded-full border border-solid border-purple-800/75 bg-white transition dragging:bg-purple-100 outline-hidden focus-visible:ring-2 ring-black&quot; /&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           &amp;lt;/&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         )}
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;/SliderTrack&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &amp;lt;/Slider&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &amp;lt;/div&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; );
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s break down the key parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The CSS classes &lt;code class=&quot;language-text&quot;&gt;absolute h-2 top-[50%] translate-y-[-50%] rounded-full bg-white&lt;/code&gt;: Positions and styles the fill element - absolute positioning, 8px height, using vertically centered trick of translate-y-[50%], rounded corners, and white background&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The width calculation:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state.getThumbPercent(0)&lt;/code&gt; returns the position of the first thumb as a decimal (0 to 1)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;* 100&lt;/code&gt; converts the decimal to a percentage&lt;/li&gt;
&lt;li&gt;This creates a dynamic width that matches the thumb’s position&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, if the slider value is 30 (out of 100):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state.getThumbPercent(0)&lt;/code&gt; returns 0.3 (30%)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;0.3 * 100&lt;/code&gt; = 30&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;30 + &apos;%&apos;&lt;/code&gt; = “30%”&lt;/li&gt;
&lt;li&gt;So the fill div will be 30% of the track’s width&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here’s the result:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ed7af9c4db3e9afd179675f5aef1c644/0a374/one-thumb-slider-filled.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBP/EABUBAQEAAAAAAAAAAAAAAAAAAAMC/9oADAMBAAIQAxAAAAFl7M0nQOP/xAAZEAADAQEBAAAAAAAAAAAAAAAAARECEiL/2gAIAQEAAQUCsfbMesIiEf/EABkRAQACAwAAAAAAAAAAAAAAAAEAAhESQf/aAAgBAwEBPwGupyNBcz//xAAXEQADAQAAAAAAAAAAAAAAAAAAAQIS/9oACAECAQE/AXaNH//EABkQAAIDAQAAAAAAAAAAAAAAAAABEBExQf/aAAgBAQAGPwKlSOCcYo//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhMVGBkeH/2gAIAQEAAT8htaYjcefzLobZm5B6h86ACgon/9oADAMBAAIAAwAAABBHH//EABcRAQEBAQAAAAAAAAAAAAAAAAERACH/2gAIAQMBAT8QkKHMBy7/xAAVEQEBAAAAAAAAAAAAAAAAAAAREP/aAAgBAgEBPxAgT//EABwQAQEAAgIDAAAAAAAAAAAAAAERACFBUTFhkf/aAAgBAQABPxCCSNhOTvA6Jdxgi8GwnLgPU1Q4o38eAQB4AgZ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;One Thumb Slider Filled&quot;
        title=&quot;One Thumb Slider Filled&quot;
        src=&quot;/static/ed7af9c4db3e9afd179675f5aef1c644/6a068/one-thumb-slider-filled.jpg&quot;
        srcset=&quot;/static/ed7af9c4db3e9afd179675f5aef1c644/09b79/one-thumb-slider-filled.jpg 240w,
/static/ed7af9c4db3e9afd179675f5aef1c644/7cc5e/one-thumb-slider-filled.jpg 480w,
/static/ed7af9c4db3e9afd179675f5aef1c644/6a068/one-thumb-slider-filled.jpg 960w,
/static/ed7af9c4db3e9afd179675f5aef1c644/644c5/one-thumb-slider-filled.jpg 1440w,
/static/ed7af9c4db3e9afd179675f5aef1c644/0f98f/one-thumb-slider-filled.jpg 1920w,
/static/ed7af9c4db3e9afd179675f5aef1c644/0a374/one-thumb-slider-filled.jpg 2166w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;two-thumbs-slider&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#two-thumbs-slider&quot; aria-label=&quot;two thumbs slider permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Two thumbs slider&lt;/h3&gt;
&lt;p&gt;For a range slider with two thumbs, we need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pass an array of two values as &lt;code class=&quot;language-text&quot;&gt;defaultValue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Map through the values to render multiple thumbs and format the output&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here’s how we implement it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;export function TwoThumbsSlider({ fill }: TwoThumbsSliderProps) {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return (
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &amp;lt;div className=&quot;bg-linear-to-r from-blue-600 to-yellow-600 p-12 rounded-lg flex justify-center&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      &amp;lt;Slider defaultValue={[30, 65]} className=&quot;w-[250px]&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;div className=&quot;flex text-white&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         &amp;lt;Label className=&quot;flex-1 text-left&quot;&gt;Two Thumbs Slider&amp;lt;/Label&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;          &amp;lt;SliderOutput&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;            {({ state }) =&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;              state.values
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                .map((_, i) =&gt; state.getThumbValueLabel(i))
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                .join(&apos; – &apos;)
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;            }
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;          &amp;lt;/SliderOutput&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;/div&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;SliderTrack className=&quot;relative w-full h-7&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         {({ state }) =&gt; (
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           &amp;lt;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             &amp;lt;div className=&quot;absolute h-2 top-[50%] translate-y-[-50%] w-full rounded-full bg-white/40&quot; /&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             {fill &amp;amp;&amp;amp; (
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;               &amp;lt;div
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;                 className=&quot;absolute h-2 top-[50%] translate-y-[-50%] rounded-full bg-white&quot;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;                 style={getSliderFillStyles(state)}
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;               /&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             )}
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;              {state.values.map((_, i) =&gt; (
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                &amp;lt;SliderThumb
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                  className=&quot;h-7 w-7 top-[50%] rounded-full border border-solid border-purple-800/75 bg-white transition 
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                    dragging:bg-purple-100 outline-hidden focus-visible:ring-2 ring-black&quot;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                  key={i}
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                  index={i}
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                /&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;              ))}
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           &amp;lt;/&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         )}
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &amp;lt;/SliderTrack&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &amp;lt;/Slider&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &amp;lt;/div&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; );
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Key differences from the single thumb slider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;defaultValue&lt;/code&gt; is now an array &lt;code class=&quot;language-text&quot;&gt;[30, 65]&lt;/code&gt; for two thumbs&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SliderOutput&lt;/code&gt; uses &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; to show both values joined with a dash&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;SliderThumb&lt;/code&gt; is rendered using &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; with an &lt;code class=&quot;language-text&quot;&gt;index&lt;/code&gt; prop&lt;/li&gt;
&lt;li&gt;The fill background uses &lt;code class=&quot;language-text&quot;&gt;getSliderFillStyles&lt;/code&gt; to calculate width and position between thumbs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s the magic function that handles the fill styles for both single and double thumbs:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSliderFillStyles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SliderState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      width&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getThumbPercent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;0%&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    width&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getThumbPercent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getThumbPercent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getThumbPercent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s break down how it works with our example values &lt;code class=&quot;language-text&quot;&gt;[30, 65]&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For two thumbs, it calculates:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;(0.65 - 0.30) * 100 = 35%&lt;/code&gt; (fills 35% of the track)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;0.30 * 100 = 30%&lt;/code&gt; (starts at 30% from the left)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;This creates a fill that spans exactly between the two thumbs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here’s the result:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e3db6da6f4af057cdd72d56afd524e6c/0a374/two-thumbs-slider-filled.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAAB0S8pcaQ//8QAGRABAQEAAwAAAAAAAAAAAAAAAgEAAxIh/9oACAEBAAEFAm1OTusPSpMZNN//xAAWEQEBAQAAAAAAAAAAAAAAAAAAEQH/2gAIAQMBAT8Bka//xAAYEQEBAAMAAAAAAAAAAAAAAAABAAIhMf/aAAgBAgEBPwHJE1HL/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAExERD/2gAIAQEABj8Ca1FRpCc//8QAGxABAAICAwAAAAAAAAAAAAAAAQAREDFRcZH/2gAIAQEAAT8hEoByYO5rcU2i+ojY8gAoKn//2gAMAwEAAgADAAAAELDv/8QAFxEBAAMAAAAAAAAAAAAAAAAAAQARIf/aAAgBAwEBPxAbt2abJ//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAgEBPxAUDG2Ab//EABoQAQEBAQEBAQAAAAAAAAAAAAERAEEhMYH/2gAIAQEAAT8QWuwEOh3BlU9ngZRVVsJ3JWV9ULnGn8Y2AHDf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Two Thumbs Slider Filled&quot;
        title=&quot;Two Thumbs Slider Filled&quot;
        src=&quot;/static/e3db6da6f4af057cdd72d56afd524e6c/6a068/two-thumbs-slider-filled.jpg&quot;
        srcset=&quot;/static/e3db6da6f4af057cdd72d56afd524e6c/09b79/two-thumbs-slider-filled.jpg 240w,
/static/e3db6da6f4af057cdd72d56afd524e6c/7cc5e/two-thumbs-slider-filled.jpg 480w,
/static/e3db6da6f4af057cdd72d56afd524e6c/6a068/two-thumbs-slider-filled.jpg 960w,
/static/e3db6da6f4af057cdd72d56afd524e6c/644c5/two-thumbs-slider-filled.jpg 1440w,
/static/e3db6da6f4af057cdd72d56afd524e6c/0f98f/two-thumbs-slider-filled.jpg 1920w,
/static/e3db6da6f4af057cdd72d56afd524e6c/0a374/two-thumbs-slider-filled.jpg 2166w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Note&lt;/strong&gt;: In this demo, I’ve separated the single and double thumb sliders into different components (&lt;code class=&quot;language-text&quot;&gt;OneThumbSlider&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;TwoThumbsSlider&lt;/code&gt;) to make the differences clearer. In a real application, you can use a single slider component that handles both cases - just pass either a single value or an array of two values to &lt;code class=&quot;language-text&quot;&gt;defaultValue&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;why-this-matters&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-this-matters&quot; aria-label=&quot;why this matters permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;As an Engineering Manager, I believe in leading by example and helping my team solve technical challenges. This kind of UI enhancement might seem small, but it makes a big difference in user experience. Plus, it’s a great way to stay connected with the technical side of things while supporting my team’s growth!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Learn Angular Signals by implementing your own]]></title><link>https://trungvose.comangular-signals-implementation/</link><guid isPermaLink="false">https://trungvose.comangular-signals-implementation/</guid><pubDate>Tue, 01 Apr 2025 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://angular.dev/essentials/signals&quot;&gt;Angular Signals&lt;/a&gt; mark a major shift in how change detection works in Angular. They give Angular a proper reactivity system — no longer relying heavily on Zone.js.&lt;/p&gt;
&lt;p&gt;But what is &lt;em&gt;reactivity&lt;/em&gt;, really?&lt;/p&gt;
&lt;p&gt;At its core, reactivity is about &lt;strong&gt;keeping your data and UI in sync&lt;/strong&gt;. When data changes, the UI should update. When the UI triggers a change, the data should reflect that. It is a two-way relationship.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Signals&lt;/strong&gt; are Angular’s new primitive for managing this relationship. A signal wraps a value and notifies anything that depends on it when that value changes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In Angular’s own words:
A signal is a lightweight wrapper around a value. Angular tracks where signals are read and when they are updated. This allows Angular to respond to state changes and update the DOM efficiently. This behaviour is what we call &lt;em&gt;reactivity&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A basic Signals usage in Angular looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Signals are getter functions — calling them reads their value&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;The count is: &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Update the value&lt;/span&gt;
count&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Update based on the previous value&lt;/span&gt;
count&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There is also &lt;code class=&quot;language-text&quot;&gt;computed()&lt;/code&gt; for derived values:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; doubleCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These are lazily evaluated and memoised — Angular only re-calculates &lt;code class=&quot;language-text&quot;&gt;doubleCount&lt;/code&gt; when &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; changes.&lt;/p&gt;
&lt;p&gt;Let implement a minimal version of &lt;code class=&quot;language-text&quot;&gt;signal&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;computed&lt;/code&gt; from scratch to understand how they work.&lt;/p&gt;
&lt;h2 id=&quot;building-a-simple-signals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#building-a-simple-signals&quot; aria-label=&quot;building a simple signals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Building a simple Signals&lt;/h2&gt;
&lt;h3 id=&quot;step-0-define-types&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-0-define-types&quot; aria-label=&quot;step 0 define types permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 0: Define types&lt;/h3&gt;
&lt;p&gt;We use TypeScript to define two types:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Signal&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WritableSignal&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Signal&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Signal&amp;lt;T&gt;&lt;/code&gt; is just a getter function. A &lt;code class=&quot;language-text&quot;&gt;WritableSignal&amp;lt;T&gt;&lt;/code&gt; is one that also has a set() method.&lt;/p&gt;
&lt;h3 id=&quot;step-1-a-basic-signal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-a-basic-signal&quot; aria-label=&quot;step 1 a basic signal permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: A basic signal()&lt;/h3&gt;
&lt;p&gt;This is a signal that holds a value, lets you read it&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initial&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WritableSignal&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; initial&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getValue&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  getValue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newValue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; getValue
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;getValue&lt;/code&gt; is just a function that returns the current value.&lt;/li&gt;
&lt;li&gt;We attach a &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; method to it so it can also update the value.&lt;/li&gt;
&lt;li&gt;This works because in JavaScript, functions are objects. You can attach extra properties to a function just like you would with any object. In this case, &lt;code class=&quot;language-text&quot;&gt;getValue.set&lt;/code&gt; is just a property we add to the function.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the above code, the following example will work&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Count is:&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;logCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Count is: 0&lt;/span&gt;

count&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;logCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Count is: 3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is your cleaned up and reflowed &lt;strong&gt;Step 2&lt;/strong&gt;, with duplications removed and the structure improved for easier reading:&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-implement-computed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-implement-computed&quot; aria-label=&quot;step 2 implement computed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2. Implement computed&lt;/h3&gt;
&lt;p&gt;A computed signal derives its value from other signals. It is a read-only signal whose value is calculated by a function you provide.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Signal&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getValue&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; getValue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this implementation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We invoke &lt;code class=&quot;language-text&quot;&gt;fn()&lt;/code&gt; once during creation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This means the computed value is calculated &lt;strong&gt;only once&lt;/strong&gt; when &lt;code class=&quot;language-text&quot;&gt;computed()&lt;/code&gt; is called, and the result is cached in &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The returned &lt;code class=&quot;language-text&quot;&gt;getValue()&lt;/code&gt; function always returns this cached value.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might wonder: why not write &lt;code class=&quot;language-text&quot;&gt;const getValue = () =&gt; fn();&lt;/code&gt; and let it recalculate every time?&lt;/p&gt;
&lt;p&gt;Here is why we do not:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Real computed signals &lt;strong&gt;cache&lt;/strong&gt; their values and recalculate &lt;strong&gt;only when dependencies change&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If we call &lt;code class=&quot;language-text&quot;&gt;fn()&lt;/code&gt; inside &lt;code class=&quot;language-text&quot;&gt;getValue()&lt;/code&gt;, it will re-run on every access, even if the underlying signals have not changed.&lt;/li&gt;
&lt;li&gt;It removes any form of caching or memoisation, causing redundant computations.&lt;/li&gt;
&lt;li&gt;Most importantly, it breaks the reactive model — in signals, updates are &lt;strong&gt;pushed&lt;/strong&gt; (by dependency changes), not pulled (by re-accessing).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is an example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; doubleCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doubleCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;

count&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doubleCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2 (still old value!)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Why does &lt;code class=&quot;language-text&quot;&gt;doubleCount()&lt;/code&gt; still return 2 after updating &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt;?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Because &lt;code class=&quot;language-text&quot;&gt;fn()&lt;/code&gt; ran only once, and we are always returning the cached value.&lt;/li&gt;
&lt;li&gt;The computed signal has &lt;strong&gt;no way of knowing&lt;/strong&gt; that &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; changed — signals do not notify anyone yet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To fix this, we need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Let signals track who depends on them&lt;/li&gt;
&lt;li&gt;Notify those dependents (subscribers) when the value changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is what we will address in &lt;strong&gt;Step 3&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-3-add-subscriber-tracking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-add-subscriber-tracking&quot; aria-label=&quot;step 3 add subscriber tracking permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3. Add subscriber tracking&lt;/h3&gt;
&lt;p&gt;In Step 2, we saw that even after updating the original signal, the computed value does not change. To make this reactive, we need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep track of which computed function depends on which signals&lt;/li&gt;
&lt;li&gt;Notify those functions when a signal’s value changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let update our &lt;code class=&quot;language-text&quot;&gt;signal&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;computed&lt;/code&gt; implementations to support that.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;First, we define a global &lt;code class=&quot;language-text&quot;&gt;currentSubscriber&lt;/code&gt; variable. When a computed function runs, it registers itself as the &lt;code class=&quot;language-text&quot;&gt;currentSubscriber&lt;/code&gt;. Then when a signal is read, it stores this subscriber in its list.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentSubscriber&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Update &lt;code class=&quot;language-text&quot;&gt;signal()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;function signal&amp;lt;T&gt;(initial: T): WritableSignal&amp;lt;T&gt; {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; let value = initial;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; const subscribers = new Set&amp;lt;() =&gt; void&gt;();
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; const getValue = () =&gt; {
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    if (currentSubscriber) {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      subscribers.add(currentSubscriber);
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    }
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   return value;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; };
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; getValue.set = (newValue: T) =&gt; {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   value = newValue;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    for (const sub of subscribers) {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      sub();
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    }
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; };
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return getValue as WritableSignal&amp;lt;T&gt;;
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now update &lt;code class=&quot;language-text&quot;&gt;computed()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;function computed&amp;lt;T&gt;(fn: () =&gt; T): Signal&amp;lt;T&gt; {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; let value: T;
&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  const recompute = () =&gt; {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    currentSubscriber = recompute;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    value = fn();
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    currentSubscriber = null;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  };
&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  recompute();
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; const getValue = () =&gt; value;
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return getValue;
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; doubleCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doubleCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;

count&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doubleCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-works&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-this-works&quot; aria-label=&quot;why this works permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why this works&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;When &lt;code class=&quot;language-text&quot;&gt;computed(fn)&lt;/code&gt; runs, it sets &lt;code class=&quot;language-text&quot;&gt;currentSubscriber&lt;/code&gt; to its &lt;code class=&quot;language-text&quot;&gt;recompute&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Any signals accessed during &lt;code class=&quot;language-text&quot;&gt;fn()&lt;/code&gt; will store &lt;code class=&quot;language-text&quot;&gt;recompute&lt;/code&gt; as a subscriber.&lt;/li&gt;
&lt;li&gt;Later, when &lt;code class=&quot;language-text&quot;&gt;count.set(5)&lt;/code&gt; is called, the &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; signal notifies its subscribers, which includes &lt;code class=&quot;language-text&quot;&gt;recompute&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;So &lt;code class=&quot;language-text&quot;&gt;recompute()&lt;/code&gt; is called, and &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; is updated.&lt;/li&gt;
&lt;li&gt;Now &lt;code class=&quot;language-text&quot;&gt;doubleCount()&lt;/code&gt; will return the updated value.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the minimal reactive system: signals track dependencies via subscriptions, and computed values get updated automatically.&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source Code&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-signals-implementation?ctl=1&amp;embed=1&amp;file=src%2Fmain.ts&amp;view=editor&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=cJ7AuQUBmA4&quot;&gt;Learn Angular Signals By Writing Your Own | Corbin Crutchley&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Cursor: Customize Your Sidebar Like VS Code]]></title><link>https://trungvose.comcursor-customize-sidebar-vscode-style/</link><guid isPermaLink="false">https://trungvose.comcursor-customize-sidebar-vscode-style/</guid><pubDate>Sun, 02 Mar 2025 01:30:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’re transitioning from VS Code to Cursor Editor and miss the familiar sidebar layout, you’re not alone. In VS Code, the sidebar is on the left side and has that small but helpful git icon that shows how many files have been changed. In Cursor, if you collapse the sidebar, you can’t access it anymore since it moves to the top.&lt;/p&gt;
&lt;p&gt;Let’s compare the default Cursor layout with VS Code’s interface:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b009a5469d18583bb5c452f7a7dd68c7/cecd0/cursor-sidebar-vscode-like-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHkFki0qn//xAAZEAACAwEAAAAAAAAAAAAAAAABAgAREiD/2gAIAQEAAQUC4VRWBP/EABcRAQEBAQAAAAAAAAAAAAAAAAEAERL/2gAIAQMBAT8BXLsv/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAREv/aAAgBAgEBPwENuG//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhYaH/2gAIAQEAAT8hqpcKNaMq2jj6f//aAAwDAQACAAMAAAAQ0w//xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QAaw//8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/EEWEP//EABwQAAICAgMAAAAAAAAAAAAAAAABESFBUTFx8P/aAAgBAQABPxBqtdZEsa5xYl0hU+ydi7y9nmx//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cursor VS Code Sidebar&quot;
        title=&quot;Cursor VS Code Sidebar&quot;
        src=&quot;/static/b009a5469d18583bb5c452f7a7dd68c7/6a068/cursor-sidebar-vscode-like-01.jpg&quot;
        srcset=&quot;/static/b009a5469d18583bb5c452f7a7dd68c7/09b79/cursor-sidebar-vscode-like-01.jpg 240w,
/static/b009a5469d18583bb5c452f7a7dd68c7/7cc5e/cursor-sidebar-vscode-like-01.jpg 480w,
/static/b009a5469d18583bb5c452f7a7dd68c7/6a068/cursor-sidebar-vscode-like-01.jpg 960w,
/static/b009a5469d18583bb5c452f7a7dd68c7/644c5/cursor-sidebar-vscode-like-01.jpg 1440w,
/static/b009a5469d18583bb5c452f7a7dd68c7/0f98f/cursor-sidebar-vscode-like-01.jpg 1920w,
/static/b009a5469d18583bb5c452f7a7dd68c7/cecd0/cursor-sidebar-vscode-like-01.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-to-make-cursors-sidebar-more-like-vs-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-make-cursors-sidebar-more-like-vs-code&quot; aria-label=&quot;how to make cursors sidebar more like vs code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to Make Cursor’s Sidebar More Like VS Code&lt;/h2&gt;
&lt;h3 id=&quot;1-open-up-cursor-settings&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-open-up-cursor-settings&quot; aria-label=&quot;1 open up cursor settings permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Open Up Cursor Settings&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Launch Cursor Editor&lt;/li&gt;
&lt;li&gt;Press ⌘ Shift P and type in &lt;code class=&quot;language-text&quot;&gt;Preferences: Open User Settings&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/41060391e5820dc6533a517008fe9ff4/1b11e/cursor-sidebar-vscode-like-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.58333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQACBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeQOBiP/xAAVEAEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAQABBQJh/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAEhIDGB/9oACAEBAAE/IVLBu7R3D//aAAwDAQACAAMAAAAQYA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAADAAEFAAAAAAAAAAAAAAAAARExECFBYXH/2gAIAQEAAT8QbZknJa4dIaTdgeHhbp//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cursor VS Code Sidebar&quot;
        title=&quot;Cursor VS Code Sidebar&quot;
        src=&quot;/static/41060391e5820dc6533a517008fe9ff4/6a068/cursor-sidebar-vscode-like-02.jpg&quot;
        srcset=&quot;/static/41060391e5820dc6533a517008fe9ff4/09b79/cursor-sidebar-vscode-like-02.jpg 240w,
/static/41060391e5820dc6533a517008fe9ff4/7cc5e/cursor-sidebar-vscode-like-02.jpg 480w,
/static/41060391e5820dc6533a517008fe9ff4/6a068/cursor-sidebar-vscode-like-02.jpg 960w,
/static/41060391e5820dc6533a517008fe9ff4/644c5/cursor-sidebar-vscode-like-02.jpg 1440w,
/static/41060391e5820dc6533a517008fe9ff4/0f98f/cursor-sidebar-vscode-like-02.jpg 1920w,
/static/41060391e5820dc6533a517008fe9ff4/1b11e/cursor-sidebar-vscode-like-02.jpg 2874w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-change-where-the-sidebar-shows-up&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-change-where-the-sidebar-shows-up&quot; aria-label=&quot;2 change where the sidebar shows up permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Change Where the Sidebar Shows Up&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Search for &lt;code class=&quot;language-text&quot;&gt;activity bar&lt;/code&gt; in the settings search box&lt;/li&gt;
&lt;li&gt;Look for the &lt;code class=&quot;language-text&quot;&gt;Activity Bar: Orientation&lt;/code&gt; setting&lt;/li&gt;
&lt;li&gt;Switch it from &lt;code class=&quot;language-text&quot;&gt;horizontal&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;vertical&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c8d493c566d11e818dd4da36c1406394/7c4bb/cursor-sidebar-vscode-like-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAePLTIP/xAAVEAEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAQABBQJj/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAFxABAQEBAAAAAAAAAAAAAAAAAQAQMf/aAAgBAQABPyHsyuGb/9oADAMBAAIAAwAAABDAD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAAMBAQEBAAAAAAAAAAAAAAABIRGBMXH/2gAIAQEAAT8QVSyiY8zOjDunt/Doln//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cursor VS Code Sidebar&quot;
        title=&quot;Cursor VS Code Sidebar&quot;
        src=&quot;/static/c8d493c566d11e818dd4da36c1406394/6a068/cursor-sidebar-vscode-like-03.jpg&quot;
        srcset=&quot;/static/c8d493c566d11e818dd4da36c1406394/09b79/cursor-sidebar-vscode-like-03.jpg 240w,
/static/c8d493c566d11e818dd4da36c1406394/7cc5e/cursor-sidebar-vscode-like-03.jpg 480w,
/static/c8d493c566d11e818dd4da36c1406394/6a068/cursor-sidebar-vscode-like-03.jpg 960w,
/static/c8d493c566d11e818dd4da36c1406394/644c5/cursor-sidebar-vscode-like-03.jpg 1440w,
/static/c8d493c566d11e818dd4da36c1406394/0f98f/cursor-sidebar-vscode-like-03.jpg 1920w,
/static/c8d493c566d11e818dd4da36c1406394/7c4bb/cursor-sidebar-vscode-like-03.jpg 4029w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-check-it-out&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-check-it-out&quot; aria-label=&quot;3 check it out permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Check It Out&lt;/h3&gt;
&lt;p&gt;Now your sidebar sits on the left, just like in VS Code!&lt;/p&gt;
&lt;h2 id=&quot;bonus-tip-make-cursor-look-more-like-vs-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bonus-tip-make-cursor-look-more-like-vs-code&quot; aria-label=&quot;bonus tip make cursor look more like vs code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bonus Tip: Make Cursor Look More Like VS Code&lt;/h2&gt;
&lt;p&gt;I found Cursor’s default theme (&lt;code class=&quot;language-text&quot;&gt;Anysphere Dark&lt;/code&gt;) a bit too dark for my taste. If you’re like me and prefer VS Code’s look, here’s how to change it:&lt;/p&gt;
&lt;h3 id=&quot;1-get-to-the-theme-settings&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-get-to-the-theme-settings&quot; aria-label=&quot;1 get to the theme settings permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Get to the Theme Settings&lt;/h3&gt;
&lt;p&gt;Open up the command palette again (⌘ Shift P) and look for &lt;code class=&quot;language-text&quot;&gt;Preferences: Color Theme&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6f2b274cde21e9e5b5f2c868526d64af/4392e/cursor-sidebar-vscode-like-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeTmQA//xAAVEAEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAQABBQIr/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBABAAMBAAAAAAAAAAAAAAAAEQABEEH/2gAIAQEAAT8hBLXOb//aAAwDAQACAAMAAAAQsw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAACAgMBAAAAAAAAAAAAAAABEQAhEDFRcf/aAAgBAQABPxBFgLHITFryI8MOX//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cursor VS Code Sidebar&quot;
        title=&quot;Cursor VS Code Sidebar&quot;
        src=&quot;/static/6f2b274cde21e9e5b5f2c868526d64af/6a068/cursor-sidebar-vscode-like-04.jpg&quot;
        srcset=&quot;/static/6f2b274cde21e9e5b5f2c868526d64af/09b79/cursor-sidebar-vscode-like-04.jpg 240w,
/static/6f2b274cde21e9e5b5f2c868526d64af/7cc5e/cursor-sidebar-vscode-like-04.jpg 480w,
/static/6f2b274cde21e9e5b5f2c868526d64af/6a068/cursor-sidebar-vscode-like-04.jpg 960w,
/static/6f2b274cde21e9e5b5f2c868526d64af/644c5/cursor-sidebar-vscode-like-04.jpg 1440w,
/static/6f2b274cde21e9e5b5f2c868526d64af/0f98f/cursor-sidebar-vscode-like-04.jpg 1920w,
/static/6f2b274cde21e9e5b5f2c868526d64af/4392e/cursor-sidebar-vscode-like-04.jpg 3417w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-pick-vs-codes-theme&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-pick-vs-codes-theme&quot; aria-label=&quot;2 pick vs codes theme permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Pick VS Code’s Theme&lt;/h3&gt;
&lt;p&gt;Choose &lt;code class=&quot;language-text&quot;&gt;Dark+ (Default Dark+)&lt;/code&gt; from the list&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/093ad374a42d7344b84acd6034546cd5/2347c/cursor-sidebar-vscode-like-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeXlAg//xAAXEAADAQAAAAAAAAAAAAAAAAAAESBB/9oACAEBAAEFAsHH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAERMRD/2gAIAQEAAT8hrgrG10qi0vP/2gAMAwEAAgADAAAAEMgP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAAMAAwAAAAAAAAAAAAAAAQARITFRYf/aAAgBAQABPxAtjbOWdqwK1LidEp7Ftuf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cursor VS Code Sidebar&quot;
        title=&quot;Cursor VS Code Sidebar&quot;
        src=&quot;/static/093ad374a42d7344b84acd6034546cd5/6a068/cursor-sidebar-vscode-like-05.jpg&quot;
        srcset=&quot;/static/093ad374a42d7344b84acd6034546cd5/09b79/cursor-sidebar-vscode-like-05.jpg 240w,
/static/093ad374a42d7344b84acd6034546cd5/7cc5e/cursor-sidebar-vscode-like-05.jpg 480w,
/static/093ad374a42d7344b84acd6034546cd5/6a068/cursor-sidebar-vscode-like-05.jpg 960w,
/static/093ad374a42d7344b84acd6034546cd5/644c5/cursor-sidebar-vscode-like-05.jpg 1440w,
/static/093ad374a42d7344b84acd6034546cd5/2347c/cursor-sidebar-vscode-like-05.jpg 1788w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And there you have it! Now your Cursor looks pretty much identical to VS Code&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c35e3b17a6992127663963b3358c8ded/cecd0/cursor-sidebar-vscode-like-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAID/9oADAMBAAIQAxAAAAHIVLzkVj//xAAXEAADAQAAAAAAAAAAAAAAAAAAAhIQ/9oACAEBAAEFAthSFIU//8QAFhEAAwAAAAAAAAAAAAAAAAAAAAET/9oACAEDAQE/AaIoj//EABcRAAMBAAAAAAAAAAAAAAAAAAABEhP/2gAIAQIBAT8BhmbP/8QAFhABAQEAAAAAAAAAAAAAAAAAADEQ/9oACAEBAAY/AtiI/8QAFxABAAMAAAAAAAAAAAAAAAAAAAER8P/aAAgBAQABPyG1rYSwlhL/2gAMAwEAAgADAAAAEO8//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxAD/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxAj/8QAGxAAAwACAwAAAAAAAAAAAAAAAAERITGxwfH/2gAIAQEAAT8QaV5Iiu2h5YHu5C07B+wf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cursor VS Code Sidebar&quot;
        title=&quot;Cursor VS Code Sidebar&quot;
        src=&quot;/static/c35e3b17a6992127663963b3358c8ded/6a068/cursor-sidebar-vscode-like-06.jpg&quot;
        srcset=&quot;/static/c35e3b17a6992127663963b3358c8ded/09b79/cursor-sidebar-vscode-like-06.jpg 240w,
/static/c35e3b17a6992127663963b3358c8ded/7cc5e/cursor-sidebar-vscode-like-06.jpg 480w,
/static/c35e3b17a6992127663963b3358c8ded/6a068/cursor-sidebar-vscode-like-06.jpg 960w,
/static/c35e3b17a6992127663963b3358c8ded/644c5/cursor-sidebar-vscode-like-06.jpg 1440w,
/static/c35e3b17a6992127663963b3358c8ded/0f98f/cursor-sidebar-vscode-like-06.jpg 1920w,
/static/c35e3b17a6992127663963b3358c8ded/cecd0/cursor-sidebar-vscode-like-06.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[bundle install: Could not find MIME type database in the following locations]]></title><link>https://trungvose.combundle-install-could-not-find-mime-type-database-in-the-following-locations-rmagick/</link><guid isPermaLink="false">https://trungvose.combundle-install-could-not-find-mime-type-database-in-the-following-locations-rmagick/</guid><pubDate>Wed, 26 Feb 2025 13:30:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;I am running &lt;code class=&quot;language-text&quot;&gt;bundle install&lt;/code&gt; on a Ruby project and I got the following error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Using dry-validation 1.10.0
Installing mimemagic 0.4.3 with native extensions
Installing rmagick 5.5.0 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /Users/trung.vo/.asdf/installs/ruby/3.1.5/lib/ruby/gems/3.1.0/gems/mimemagic-0.4.3/ext/mimemagic
/Users/trung.vo/.asdf/installs/ruby/3.1.5/bin/ruby -I/Users/trung.vo/.asdf/installs/ruby/3.1.5/lib/ruby/3.1.0 -rrubygems
/Users/trung.vo/.asdf/installs/ruby/3.1.5/lib/ruby/gems/3.1.0/gems/rake-13.2.1/exe/rake
RUBYARCHDIR\=/Users/trung.vo/.asdf/installs/ruby/3.1.5/lib/ruby/gems/3.1.0/extensions/arm64-darwin-24/3.1.0/mimemagic-0.4.3
RUBYLIBDIR\=/Users/trung.vo/.asdf/installs/ruby/3.1.5/lib/ruby/gems/3.1.0/extensions/arm64-darwin-24/3.1.0/mimemagic-0.4.3
rake aborted!
Could not find MIME type database in the following locations: [&quot;/usr/local/share/mime/packages/freedesktop.org.xml&quot;, &quot;/opt/homebrew/share/mime/packages/freedesktop.org.xml&quot;,
&quot;/opt/local/share/mime/packages/freedesktop.org.xml&quot;, &quot;/usr/share/mime/packages/freedesktop.org.xml&quot;]

Ensure you have either installed the shared-mime-info package for your distribution, or
obtain a version of freedesktop.org.xml and set FREEDESKTOP_MIME_TYPES_PATH to the location
of that file.

Tasks: TOP =&gt; default
(See full trace by running task with --trace)

rake failed, exit code 1

Gem files will remain installed in /Users/trung.vo/.asdf/installs/ruby/3.1.5/lib/ruby/gems/3.1.0/gems/mimemagic-0.4.3 for inspection.
Results logged to /Users/trung.vo/.asdf/installs/ruby/3.1.5/lib/ruby/gems/3.1.0/extensions/arm64-darwin-24/3.1.0/mimemagic-0.4.3/gem_make.out&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here’s what the error looks like in the terminal:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d9f3d1b62a6bf476bd65a19965e6c870/e1596/bundle-install-error.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeVNyZg//8QAFxAAAwEAAAAAAAAAAAAAAAAAABARAf/aAAgBAQABBQI1Uq//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAVEAEBAAAAAAAAAAAAAAAAAAAgMf/aAAgBAQAGPwKL/8QAFxABAQEBAAAAAAAAAAAAAAAAAQAQcf/aAAgBAQABPyGcBgpef//aAAwDAQACAAMAAAAQk8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhMUFRgZH/2gAIAQEAAT8QReDBW/ZMdQQYIwqj5HbP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;bundle install error&quot;
        title=&quot;bundle install error&quot;
        src=&quot;/static/d9f3d1b62a6bf476bd65a19965e6c870/6a068/bundle-install-error.jpg&quot;
        srcset=&quot;/static/d9f3d1b62a6bf476bd65a19965e6c870/09b79/bundle-install-error.jpg 240w,
/static/d9f3d1b62a6bf476bd65a19965e6c870/7cc5e/bundle-install-error.jpg 480w,
/static/d9f3d1b62a6bf476bd65a19965e6c870/6a068/bundle-install-error.jpg 960w,
/static/d9f3d1b62a6bf476bd65a19965e6c870/644c5/bundle-install-error.jpg 1440w,
/static/d9f3d1b62a6bf476bd65a19965e6c870/0f98f/bundle-install-error.jpg 1920w,
/static/d9f3d1b62a6bf476bd65a19965e6c870/e1596/bundle-install-error.jpg 2048w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;First, install the &lt;code class=&quot;language-text&quot;&gt;shared-mime-info&lt;/code&gt; package using Homebrew:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; shared-mime-info&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;After fixing the MIME type issue, you might encounter another error related to RMagick:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;An error occurred while installing rmagick (5.5.0), and Bundler cannot continue.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To fix this, install ImageMagick:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; imagemagick&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;output&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#output&quot; aria-label=&quot;output permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Output&lt;/h2&gt;
&lt;p&gt;After installing both required packages, running &lt;code class=&quot;language-text&quot;&gt;bundle install&lt;/code&gt; again should complete successfully:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Using mustermann 3.0.0
Using mimemagic 0.4.3
Using rubocop 1.63.2
Using listen 3.9.0
Using rspec 3.13.0
Using aws-sdk-s3 1.147.0
Using sidekiq-scheduler 5.0.3
Using elasticsearch 7.13.0
Using dry-validation 1.10.0
Using sinatra 3.2.0
Using guard 2.18.1
Using sinatra-contrib 3.2.0
Using guard-rack 2.2.1
Using guard-bundler 3.0.1
Using guard-sidekiq 0.1.0
Installing rmagick 5.5.0 with native extensions
Bundle complete! 50 Gemfile dependencies, 157 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wow, it works!
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0b32ad370db169dcbc26006b733d608b/08702/bundle-install-success.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcmYgL//xAAVEAEBAAAAAAAAAAAAAAAAAAACEP/aAAgBAQABBQJX/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFRABAQAAAAAAAAAAAAAAAAAAEEH/2gAIAQEAAT8hh//aAAwDAQACAAMAAAAQ9A//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxAZ/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGBAAAgMAAAAAAAAAAAAAAAAAASEQEXH/2gAIAQEAAT8QYUeT/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;bundle install success&quot;
        title=&quot;bundle install success&quot;
        src=&quot;/static/0b32ad370db169dcbc26006b733d608b/6a068/bundle-install-success.jpg&quot;
        srcset=&quot;/static/0b32ad370db169dcbc26006b733d608b/09b79/bundle-install-success.jpg 240w,
/static/0b32ad370db169dcbc26006b733d608b/7cc5e/bundle-install-success.jpg 480w,
/static/0b32ad370db169dcbc26006b733d608b/6a068/bundle-install-success.jpg 960w,
/static/0b32ad370db169dcbc26006b733d608b/644c5/bundle-install-success.jpg 1440w,
/static/0b32ad370db169dcbc26006b733d608b/0f98f/bundle-install-success.jpg 1920w,
/static/0b32ad370db169dcbc26006b733d608b/08702/bundle-install-success.jpg 2434w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Netlify Redirects vs Gatsby Redirects: My Traffic Drop 90%]]></title><link>https://trungvose.comnetlify-redirects-vs-gatsby-redirects/</link><guid isPermaLink="false">https://trungvose.comnetlify-redirects-vs-gatsby-redirects/</guid><pubDate>Fri, 10 Jan 2025 10:30:00 GMT</pubDate><content:encoded>&lt;p&gt;This blog was originally built with Jekyll, but due to the challenges of running and maintaining it locally, I migrated to Gatsby. Initially, I retained the &lt;code class=&quot;language-text&quot;&gt;/experience/:slug&lt;/code&gt; path for my blog posts to avoid losing traffic. However, I later decided to change the path to &lt;code class=&quot;language-text&quot;&gt;/blog/:slug&lt;/code&gt;, which unfortunately led to a significant 90% drop in traffic. Here’s what happened and how I attempted to fix it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2ed7f8189562c5af673a262f28643aff/1c0b3/blog-redirect-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAEDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHZsKkR/8QAFxAAAwEAAAAAAAAAAAAAAAAAAhARE//aAAgBAQABBQLMKv/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABcQAAMBAAAAAAAAAAAAAAAAAAEQERL/2gAIAQEABj8CuRX/AP/EABkQAAIDAQAAAAAAAAAAAAAAAAERABAhMf/aAAgBAQABPyFAihwqbX//2gAMAwEAAgADAAAAEPPv/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAh/9oACAEDAQE/EBF//8QAFhEBAQEAAAAAAAAAAAAAAAAAARAx/9oACAECAQE/EFXZ/8QAGhABAAIDAQAAAAAAAAAAAAAAAQAhETFBkf/aAAgBAQABPxAACmdgYvCeQatn/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Netlify Redirects vs Gatsby Redirects&quot;
        title=&quot;Netlify Redirects vs Gatsby Redirects&quot;
        src=&quot;/static/2ed7f8189562c5af673a262f28643aff/6a068/blog-redirect-01.jpg&quot;
        srcset=&quot;/static/2ed7f8189562c5af673a262f28643aff/09b79/blog-redirect-01.jpg 240w,
/static/2ed7f8189562c5af673a262f28643aff/7cc5e/blog-redirect-01.jpg 480w,
/static/2ed7f8189562c5af673a262f28643aff/6a068/blog-redirect-01.jpg 960w,
/static/2ed7f8189562c5af673a262f28643aff/644c5/blog-redirect-01.jpg 1440w,
/static/2ed7f8189562c5af673a262f28643aff/0f98f/blog-redirect-01.jpg 1920w,
/static/2ed7f8189562c5af673a262f28643aff/1c0b3/blog-redirect-01.jpg 2380w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In early December 2024, I naively added &lt;code class=&quot;language-text&quot;&gt;createRedirect&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;gatsby-node.js&lt;/code&gt; to redirect the old path to the new one.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;createPage({
&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;  path: `/experience/${slug}`,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  path: `/blog/${slug}`,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; component: slash(postTemplate),
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; context: { slug },
&lt;/span&gt;})
// Redirect from /experience/slug to /blog/slug
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; createRedirect({
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   fromPath: `/experience/${slug}`,
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   toPath: `/blog/${slug}`,
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   isPermanent: true,
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   redirectInBrowser: true,
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; });&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I suspect the traffic drop was due to Google indexing the old &lt;code class=&quot;language-text&quot;&gt;/experience/:slug&lt;/code&gt; paths and returning 404 errors because Gatsby redirects happen on the client side.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b6b3052fbebe1283c7081095f85b19c8/08702/blog-redirect-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 85.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAARABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHu6kBNoWQn/8QAFxAAAwEAAAAAAAAAAAAAAAAAAQIgEf/aAAgBAQABBQKCuz//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAY/Ah//xAAbEAACAwADAAAAAAAAAAAAAAAAARARMSFxgf/aAAgBAQABPyGjw7QhG2yq4hZH/9oADAMBAAIAAwAAABAT8AP/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAwACAwAAAAAAAAAAAAABABExIUEQYaH/2gAIAQEAAT8QE4fZSHBX2ylOAewbi42pUZOhqUAMCjxhHZ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Netlify Redirects vs Gatsby Redirects&quot;
        title=&quot;Netlify Redirects vs Gatsby Redirects&quot;
        src=&quot;/static/b6b3052fbebe1283c7081095f85b19c8/6a068/blog-redirect-02.jpg&quot;
        srcset=&quot;/static/b6b3052fbebe1283c7081095f85b19c8/09b79/blog-redirect-02.jpg 240w,
/static/b6b3052fbebe1283c7081095f85b19c8/7cc5e/blog-redirect-02.jpg 480w,
/static/b6b3052fbebe1283c7081095f85b19c8/6a068/blog-redirect-02.jpg 960w,
/static/b6b3052fbebe1283c7081095f85b19c8/644c5/blog-redirect-02.jpg 1440w,
/static/b6b3052fbebe1283c7081095f85b19c8/0f98f/blog-redirect-02.jpg 1920w,
/static/b6b3052fbebe1283c7081095f85b19c8/08702/blog-redirect-02.jpg 2434w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;When you curl the page, it still returns a 404. In a browser, you see a 404 momentarily before being redirected to the new page. The 404 errors likely caused Google to remove the &lt;code class=&quot;language-text&quot;&gt;experience&lt;/code&gt; pages from search results, significantly impacting my traffic.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a4133fc8bcf86bcf74b02c7d0cdefe93/e61b5/blog-redirect-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAc2CAr//xAAVEAEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAQABBQJv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFhABAQEAAAAAAAAAAAAAAAAAAAER/9oACAEBAAE/IYYj/9oADAMBAAIAAwAAABB7z//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAQADAQEAAAAAAAAAAAAAAAEAETEhkf/aAAgBAQABPxAnOXGmr5BRP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Curl blog path 404&quot;
        title=&quot;Curl blog path 404&quot;
        src=&quot;/static/a4133fc8bcf86bcf74b02c7d0cdefe93/6a068/blog-redirect-03.jpg&quot;
        srcset=&quot;/static/a4133fc8bcf86bcf74b02c7d0cdefe93/09b79/blog-redirect-03.jpg 240w,
/static/a4133fc8bcf86bcf74b02c7d0cdefe93/7cc5e/blog-redirect-03.jpg 480w,
/static/a4133fc8bcf86bcf74b02c7d0cdefe93/6a068/blog-redirect-03.jpg 960w,
/static/a4133fc8bcf86bcf74b02c7d0cdefe93/644c5/blog-redirect-03.jpg 1440w,
/static/a4133fc8bcf86bcf74b02c7d0cdefe93/0f98f/blog-redirect-03.jpg 1920w,
/static/a4133fc8bcf86bcf74b02c7d0cdefe93/e61b5/blog-redirect-03.jpg 2790w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To resolve this, I needed to ensure that Google indexing the old &lt;code class=&quot;language-text&quot;&gt;/experience/:slug&lt;/code&gt; paths would return a 301 redirect to the new &lt;code class=&quot;language-text&quot;&gt;/blog/:slug&lt;/code&gt; paths. I decided to use Netlify redirects, which are easy to implement. I added the new redirect rules to the &lt;code class=&quot;language-text&quot;&gt;_redirects&lt;/code&gt; file in the &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; folder.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; /experience/*              /blog/:splat&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This redirects paths like &lt;code class=&quot;language-text&quot;&gt;/experience/image-lazy-load&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/blog/image-lazy-load&lt;/code&gt; at the hosting level, returning a proper 301 HTTP code. Learn more about Netlify wildcard redirects &lt;a href=&quot;https://docs.netlify.com/routing/redirects/redirect-options/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cdd513c1868ec6c57783f7cb10e94e5f/e61b5/blog-redirect-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHNoRIP/8QAFRABAQAAAAAAAAAAAAAAAAAAARD/2gAIAQEAAQUCb//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABcQAAMBAAAAAAAAAAAAAAAAAAABERD/2gAIAQEAAT8hjIz/2gAMAwEAAgADAAAAEHgv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRAAAwEBAQAAAAAAAAAAAAAAAAERQSEx/9oACAEBAAE/ELOaNK+sUlLM6f/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Curl blog path 404&quot;
        title=&quot;Curl blog path 404&quot;
        src=&quot;/static/cdd513c1868ec6c57783f7cb10e94e5f/6a068/blog-redirect-04.jpg&quot;
        srcset=&quot;/static/cdd513c1868ec6c57783f7cb10e94e5f/09b79/blog-redirect-04.jpg 240w,
/static/cdd513c1868ec6c57783f7cb10e94e5f/7cc5e/blog-redirect-04.jpg 480w,
/static/cdd513c1868ec6c57783f7cb10e94e5f/6a068/blog-redirect-04.jpg 960w,
/static/cdd513c1868ec6c57783f7cb10e94e5f/644c5/blog-redirect-04.jpg 1440w,
/static/cdd513c1868ec6c57783f7cb10e94e5f/0f98f/blog-redirect-04.jpg 1920w,
/static/cdd513c1868ec6c57783f7cb10e94e5f/e61b5/blog-redirect-04.jpg 2790w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For now, I will push the changes and monitor the traffic to see if it recovers.&lt;/p&gt;
&lt;h2 id=&quot;the-second-attempt&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-second-attempt&quot; aria-label=&quot;the second attempt permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The second attempt&lt;/h2&gt;
&lt;p&gt;After completing the blog, I realized that a 301 status code would still prevent Google from indexing the page. Therefore, I needed another solution.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2a4a1633a17f0c3d199b4a85784a1d64/06c29/blog-redirect-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB3nSyYCf/xAAZEAACAwEAAAAAAAAAAAAAAAACEAABESH/2gAIAQEAAQUCXZYluEv/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAEBAQEAAAAAAAAAAAAAAAAAITEQ/9oACAEBAAY/AuVrVf/EABoQAQEAAgMAAAAAAAAAAAAAAAEAEUEQUYH/2gAIAQEAAT8hDg6A9kJMSD1jIbN//9oADAMBAAIAAwAAABCA/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAQACAwAAAAAAAAAAAAABEQAhUXGBkf/aAAgBAQABPxCHf3eslal4lcZ0KNCvGLg3kumjOwJv/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Curl blog path 301&quot;
        title=&quot;Curl blog path 301&quot;
        src=&quot;/static/2a4a1633a17f0c3d199b4a85784a1d64/6a068/blog-redirect-05.jpg&quot;
        srcset=&quot;/static/2a4a1633a17f0c3d199b4a85784a1d64/09b79/blog-redirect-05.jpg 240w,
/static/2a4a1633a17f0c3d199b4a85784a1d64/7cc5e/blog-redirect-05.jpg 480w,
/static/2a4a1633a17f0c3d199b4a85784a1d64/6a068/blog-redirect-05.jpg 960w,
/static/2a4a1633a17f0c3d199b4a85784a1d64/644c5/blog-redirect-05.jpg 1440w,
/static/2a4a1633a17f0c3d199b4a85784a1d64/06c29/blog-redirect-05.jpg 1912w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I decided to rewrite the URL from &lt;code class=&quot;language-text&quot;&gt;/experience/:slug&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/blog/:slug&lt;/code&gt; using Netlify’s &lt;code class=&quot;language-text&quot;&gt;_redirects&lt;/code&gt; file. Here’s how I did it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you assign an HTTP &lt;a href=&quot;https://docs.netlify.com/routing/redirects/rewrites-proxies/&quot;&gt;status code of 200&lt;/a&gt; to a redirect rule, it becomes a rewrite. This means that the URL in the visitor’s address bar remains the same, while Netlify’s servers fetch the new location behind the scenes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; /experience/*              /blog/:splat
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; /experience/*              /blog/:splat  200&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is similar to the redirect rule, but by adding the status code 200, it becomes a rewrite. This means that when I curl the page, it will return a 200 status code and could potentially be indexed by Google.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9701584961f97b818d7bca539bd30903/37e78/blog-redirect-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAc2Eihh//8QAFRABAQAAAAAAAAAAAAAAAAAAASD/2gAIAQEAAQUCa//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAIDAAAAAAAAAAAAAAAAAAAREEFh/9oACAEBAAE/IUFopo//2gAMAwEAAgADAAAAEFMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHRABAAEDBQAAAAAAAAAAAAAAAQAQITFBUXGRof/aAAgBAQABPxA3BaO7xBBr1HLQw4n/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Curl blog path 200&quot;
        title=&quot;Curl blog path 200&quot;
        src=&quot;/static/9701584961f97b818d7bca539bd30903/6a068/blog-redirect-06.jpg&quot;
        srcset=&quot;/static/9701584961f97b818d7bca539bd30903/09b79/blog-redirect-06.jpg 240w,
/static/9701584961f97b818d7bca539bd30903/7cc5e/blog-redirect-06.jpg 480w,
/static/9701584961f97b818d7bca539bd30903/6a068/blog-redirect-06.jpg 960w,
/static/9701584961f97b818d7bca539bd30903/644c5/blog-redirect-06.jpg 1440w,
/static/9701584961f97b818d7bca539bd30903/0f98f/blog-redirect-06.jpg 1920w,
/static/9701584961f97b818d7bca539bd30903/37e78/blog-redirect-06.jpg 2670w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It appears that after implementing the changes, the traffic is starting to recover. I’m not certain if this improvement is due to the rewrite or merely coincidental. I will keep monitoring the traffic and update this blog with any new developments.&lt;/p&gt;
&lt;h3 id=&quot;update-feb-2025&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update-feb-2025&quot; aria-label=&quot;update feb 2025 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update Feb 2025&lt;/h3&gt;
&lt;p&gt;Traffic is recovering; I am now getting about 90 clicks per day compared to peak periods of 160 clicks in the past six months.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/707486f77227dd5a6e8f2da5761a47d5/8b3ab/blog-redirect-08-2025-02-15.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB17iAP//EABcQAAMBAAAAAAAAAAAAAAAAAAECAxD/2gAIAQEAAQUCElDZ/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAEDH/2gAIAQIBAT8Bp//EABcQAAMBAAAAAAAAAAAAAAAAABARIZH/2gAIAQEABj8Cd0//xAAWEAEBAQAAAAAAAAAAAAAAAAABECH/2gAIAQEAAT8hJHZX/9oADAMBAAIAAwAAABD3z//EABYRAAMAAAAAAAAAAAAAAAAAAAEQIf/aAAgBAwEBPxARf//EABYRAQEBAAAAAAAAAAAAAAAAAAEQIf/aAAgBAgEBPxBXTP/EABkQAQEBAQEBAAAAAAAAAAAAAAERAEExkf/aAAgBAQABPxBauCtPmoeFw3k3/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Netlify Redirects vs Gatsby Redirects: My Traffic Drop 90%&quot;
        title=&quot;Netlify Redirects vs Gatsby Redirects: My Traffic Drop 90%&quot;
        src=&quot;/static/707486f77227dd5a6e8f2da5761a47d5/6a068/blog-redirect-08-2025-02-15.jpg&quot;
        srcset=&quot;/static/707486f77227dd5a6e8f2da5761a47d5/09b79/blog-redirect-08-2025-02-15.jpg 240w,
/static/707486f77227dd5a6e8f2da5761a47d5/7cc5e/blog-redirect-08-2025-02-15.jpg 480w,
/static/707486f77227dd5a6e8f2da5761a47d5/6a068/blog-redirect-08-2025-02-15.jpg 960w,
/static/707486f77227dd5a6e8f2da5761a47d5/644c5/blog-redirect-08-2025-02-15.jpg 1440w,
/static/707486f77227dd5a6e8f2da5761a47d5/0f98f/blog-redirect-08-2025-02-15.jpg 1920w,
/static/707486f77227dd5a6e8f2da5761a47d5/8b3ab/blog-redirect-08-2025-02-15.jpg 2390w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Understanding staleTime and gcTime (cacheTime) in React Query]]></title><link>https://trungvose.comstale-time-vs-gc-time-cache-time-in-react-query/</link><guid isPermaLink="false">https://trungvose.comstale-time-vs-gc-time-cache-time-in-react-query/</guid><pubDate>Tue, 24 Dec 2024 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;In React Query, two key options control the freshness and caching duration of query results: &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;gcTime&lt;/code&gt; (formerly &lt;code class=&quot;language-text&quot;&gt;cacheTime&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Here’s a brief explanation of each:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt;: The duration a query result is considered fresh. While fresh, data is read from the cache only, with &lt;strong&gt;no network requests&lt;/strong&gt;. Once stale (default is instantly), data is still read from the cache, but a background refetch can happen under certain conditions.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;gcTime&lt;/code&gt;: The duration inactive queries remain in the cache before being removed. This defaults to 5 minutes. Queries become inactive when no components are observing them.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Typically, you may need to adjust &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; more often than &lt;code class=&quot;language-text&quot;&gt;gcTime&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;staletime&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#staletime&quot; aria-label=&quot;staletime permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;staleTime&lt;/h2&gt;
&lt;p&gt;To understand &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt;, consider a simple example of a hook to fetch a single vehicle:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useFetchVehicle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;vehicleId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vehicle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setVehicle&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Vehicle &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setLoading&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setError&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Vehicle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchVehicle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vehicleId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setVehicle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; Error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vehicleId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vehicle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using React Query, we can simplify this with the &lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt; hook, eliminating the need for &lt;code class=&quot;language-text&quot;&gt;useState&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &apos;@tanstack&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;react&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;query’&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useFetchVehicle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;vehicleId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    queryKey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;vehicle&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; vehicleId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function-variable function&quot;&gt;queryFn&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchVehicle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vehicleId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;default-staletime-of-0&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#default-staletime-of-0&quot; aria-label=&quot;default staletime of 0 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Default &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In the above code, &lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt; manages the loading, error, and data states. The &lt;code class=&quot;language-text&quot;&gt;queryKey&lt;/code&gt; uniquely identifies the query, and &lt;code class=&quot;language-text&quot;&gt;queryFn&lt;/code&gt; fetches the data. Since &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; is not set, it defaults to &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Notice how selecting the first item triggers an API call for &lt;code class=&quot;language-text&quot;&gt;/vehicle/1&lt;/code&gt;. Returning to the list and selecting the first item again triggers &lt;strong&gt;another API call&lt;/strong&gt;. The UI renders &lt;strong&gt;immediately&lt;/strong&gt; with cached data, but a background refetch happens due to the default &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/1cde655de1bccf948feddca015a4edfd/staleTime-zero.gif&quot; alt=&quot;staleTime&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;In DevTools, &lt;code class=&quot;language-text&quot;&gt;[&apos;vehicle&apos;, &apos;1&apos;]&lt;/code&gt; is considered stale immediately with &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; set to &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d5df58864254d1da4a98ab4dba406725/cecd0/staleTime-devtools.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAQX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGlXUwWUB//xAAZEAADAQEBAAAAAAAAAAAAAAABAgMEABP/2gAIAQEAAQUCfcFY71PZ7CiGMifCXKiqP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EAB4QAAEEAQUAAAAAAAAAAAAAAAABAhGRMwMSITFh/9oACAEBAAY/Albs69Ma2SjY5JXTbRjbRDWoh//EABkQAQEBAQEBAAAAAAAAAAAAAAERADEQIf/aAAgBAQABPyGjrSZ0MEcQiXKEF+r49DA7A3//2gAMAwEAAgADAAAAELjP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAAICAwEAAAAAAAAAAAAAAQARMVFBwdHx/9oACAEBAAE/EH4i9qXTWooYOj5EqPXBwD3FQWoFq8z4mOlNsIL3P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;staleTime&quot;
        title=&quot;staleTime&quot;
        src=&quot;/static/d5df58864254d1da4a98ab4dba406725/6a068/staleTime-devtools.jpg&quot;
        srcset=&quot;/static/d5df58864254d1da4a98ab4dba406725/09b79/staleTime-devtools.jpg 240w,
/static/d5df58864254d1da4a98ab4dba406725/7cc5e/staleTime-devtools.jpg 480w,
/static/d5df58864254d1da4a98ab4dba406725/6a068/staleTime-devtools.jpg 960w,
/static/d5df58864254d1da4a98ab4dba406725/644c5/staleTime-devtools.jpg 1440w,
/static/d5df58864254d1da4a98ab4dba406725/0f98f/staleTime-devtools.jpg 1920w,
/static/d5df58864254d1da4a98ab4dba406725/cecd0/staleTime-devtools.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;A background refetch can happen when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New instances of the query mount&lt;/li&gt;
&lt;li&gt;The window is refocused&lt;/li&gt;
&lt;li&gt;The network is reconnected&lt;/li&gt;
&lt;li&gt;The query is configured with a refetch interval&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read more from &lt;a href=&quot;https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults&quot;&gt;Important Defaults&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;setting-staletime-to-another-value&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-staletime-to-another-value&quot; aria-label=&quot;setting staletime to another value permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; to another value&lt;/h3&gt;
&lt;p&gt;Consider setting &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;30&lt;/code&gt; seconds (&lt;code class=&quot;language-text&quot;&gt;30 * 1000&lt;/code&gt; milliseconds):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;export const useFetchVehicle = (vehicleId: string | undefined) =&gt; {
  return useQuery({
    queryKey: [&apos;vehicle&apos;, vehicleId],
    queryFn: () =&gt; fetchVehicle(vehicleId),
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   staleTime: 30 * 1000,
&lt;/span&gt;  });
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, selecting the first item triggers an API call for &lt;code class=&quot;language-text&quot;&gt;/vehicle/1&lt;/code&gt;. Returning to the list and selecting the first item again &lt;strong&gt;does not&lt;/strong&gt; trigger an API call. The UI renders immediately with cached data, considered fresh for 30 seconds, with no background refetch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/7353c107036865c80b2c55ede77fd3ce/staleTime-not-zero.gif&quot; alt=&quot;useQuery not zero&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;In DevTools, &lt;code class=&quot;language-text&quot;&gt;[&apos;vehicle&apos;, &apos;1&apos;]&lt;/code&gt; is considered fresh for 30 seconds with &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; set to &lt;code class=&quot;language-text&quot;&gt;30 seconds&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3825ad0c321992dc0398a286a9f54795/cecd0/staleTime-not-zero-devtools.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEEAv/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAblcgNh//8QAGhAAAwEAAwAAAAAAAAAAAAAAAQIDAAQTFP/aAAgBAQABBQKnJmr+6OlRaqUQnrTAAb//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAdEAACAgEFAAAAAAAAAAAAAAAAAQIhkRESMTJB/9oACAEBAAY/AnFt0e4N0eC4xwdI4KWh/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARIUFR8f/aAAgBAQABPyG3FW0SjstT6rZaFL1E+dhtEPAn/9oADAMBAAIAAwAAABDAz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAECAQE/EIf/xAAdEAEAAgICAwAAAAAAAAAAAAABABEhkTHwYaHB/9oACAEBAAE/EDX6gXncpBf15gNElgpsiprWpL6ndPkzeW6QaJ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;DevTools Fresh Data&quot;
        title=&quot;DevTools Fresh Data&quot;
        src=&quot;/static/3825ad0c321992dc0398a286a9f54795/6a068/staleTime-not-zero-devtools.jpg&quot;
        srcset=&quot;/static/3825ad0c321992dc0398a286a9f54795/09b79/staleTime-not-zero-devtools.jpg 240w,
/static/3825ad0c321992dc0398a286a9f54795/7cc5e/staleTime-not-zero-devtools.jpg 480w,
/static/3825ad0c321992dc0398a286a9f54795/6a068/staleTime-not-zero-devtools.jpg 960w,
/static/3825ad0c321992dc0398a286a9f54795/644c5/staleTime-not-zero-devtools.jpg 1440w,
/static/3825ad0c321992dc0398a286a9f54795/0f98f/staleTime-not-zero-devtools.jpg 1920w,
/static/3825ad0c321992dc0398a286a9f54795/cecd0/staleTime-not-zero-devtools.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;why-the-default-staletime-is-0&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-the-default-staletime-is-0&quot; aria-label=&quot;why the default staletime is 0 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why the default &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Quoted from &lt;a href=&quot;https://ui.dev/c/query/data-synchronization&quot;&gt;UI Dev Data Synchronization&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The default &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; means every &lt;strong&gt;query is instantly considered stale&lt;/strong&gt;. This “aggressive but sane default” ensures frequent refetching, which is generally safer than fetching too infrequently.&lt;/p&gt;
&lt;p&gt;The docs define this as “aggressive but sane defaults”.&lt;/p&gt;
&lt;p&gt;Aggressive, because it means we might be refetching from the server more often than we need to, but sane because fetching too often is the lesser evil of the two options.&lt;/p&gt;
&lt;p&gt;It’s a bit like re-renders in React. Yes, we all want to minimize our application’s re-renders, but having too many is significantly better than having too little where your view could be out of sync with your application’s state.&lt;/p&gt;
&lt;p&gt;Also, if the default value weren’t 0, what would be a better default? 20 seconds? 30? 1 minute? It’s one of those cases that you can’t reliably set up for every possible situation. The answer is always &lt;code class=&quot;language-text&quot;&gt;it depends&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Specifically, it depends on the resource in question: How often is it updated? How accurate does the data displayed on your screen need to be? How collaborative is the environment you’re working in?&lt;/p&gt;
&lt;p&gt;The answer to these questions should be decided by developers on a case by case basis.&lt;/p&gt;
&lt;p&gt;If we fetch a Twitter post with all its likes and comments, it’s likely stale pretty fast. On the other hand, if we fetch exchange rates that update on a daily basis, well, our data is going to be quite accurate for some time even without refetching.&lt;/p&gt;
&lt;h2 id=&quot;cachetime-gctime&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cachetime-gctime&quot; aria-label=&quot;cachetime gctime permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;cacheTime (gcTime)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;cacheTime&lt;/code&gt; does nothing while a query is in use. It activates once the query becomes unused, removing data from the cache after the specified duration (default is 5 minutes).&lt;/p&gt;
&lt;p&gt;The time in milliseconds that unused/inactive cache data remains in memory. When a query’s cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different garbage collection times are specified, the longest one will be used.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Defaults to &lt;code class=&quot;language-text&quot;&gt;5 * 60 * 1000 (5 minutes)&lt;/code&gt; or Infinity during SSR.&lt;/li&gt;
&lt;li&gt;Note: the maximum allowed time is about 24 days. See more at &lt;a href=&quot;https://tanstack.com/query/latest/docs/framework/react/reference/useQuery#usequery&quot;&gt;useQuery&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If set to &lt;code class=&quot;language-text&quot;&gt;Infinity&lt;/code&gt;, garbage collection is disabled.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;gc&lt;/code&gt; refers to “garbage collect” time, a common term in computer science.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, the &lt;code class=&quot;language-text&quot;&gt;[&apos;vehicle&apos;, &apos;1&apos;]&lt;/code&gt; query is removed from the cache after 5 minutes of inactivity.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In React Query v5, the &lt;code class=&quot;language-text&quot;&gt;cacheTime&lt;/code&gt; option has been &lt;a href=&quot;https://tanstack.com/query/latest/docs/framework/vue/guides/migrating-to-v5#rename-cachetime-to-gctime&quot;&gt;renamed&lt;/a&gt; to &lt;code class=&quot;language-text&quot;&gt;gcTime&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tkdodo.eu/blog/practical-react-query#the-defaults-explained&quot;&gt;Practical React Query&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.dev/c/query/data-synchronization&quot;&gt;UI Dev Data Synchronization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults&quot;&gt;Important Defaults&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;creating-fast-feeling-web-apps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#creating-fast-feeling-web-apps&quot; aria-label=&quot;creating fast feeling web apps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Creating Fast-Feeling Web Apps&lt;/h2&gt;
&lt;p&gt;I recently gave a 30-minute version of my talk, &lt;em&gt;Creating Fast-Feeling Web Apps&lt;/em&gt;, at JSConfJP in Tokyo, one of the largest web developer events in Japan. The feedback from attendees was fantastic. During the talk, I covered topics like &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; in React Query and how to use &lt;code class=&quot;language-text&quot;&gt;initialData&lt;/code&gt; to deliver an instant user experience. You can check out the &lt;a href=&quot;/talks/2024-11-23-jsconfjp/&quot;&gt;slides and the recorded talk&lt;/a&gt; for more details&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Chrome DevTools Performance Panel: Analyze Your Website's Performance]]></title><link>https://trungvose.comchrome-devtools-performance/</link><guid isPermaLink="false">https://trungvose.comchrome-devtools-performance/</guid><pubDate>Thu, 14 Nov 2024 14:30:00 GMT</pubDate><content:encoded>&lt;p&gt;The Performance panel in Chrome DevTools allows you to record and analyze CPU performance profiles of your web applications. Use it to identify performance bottlenecks and optimize resource usage.&lt;/p&gt;
&lt;h3 id=&quot;key-features-of-the-performance-panel&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-features-of-the-performance-panel&quot; aria-label=&quot;key features of the performance panel permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key Features of the Performance Panel&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Record a performance profile.&lt;/li&gt;
&lt;li&gt;Adjust capture settings.&lt;/li&gt;
&lt;li&gt;Analyze performance reports.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;opening-the-performance-panel&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#opening-the-performance-panel&quot; aria-label=&quot;opening the performance panel permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Opening the Performance Panel&lt;/h2&gt;
&lt;p&gt;To open the Performance panel, launch DevTools and select the &lt;strong&gt;Performance&lt;/strong&gt; tab.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fe328b0c41e424b461dae633750c8226/9568a/performance-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdpAsD//xAAXEAEBAQEAAAAAAAAAAAAAAAABABBB/9oACAEBAAEFAt6sX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAMBAQEAAAAAAAAAAAAAAAABESExUf/aAAgBAQABPyGumrxFOUWWnB//2gAMAwEAAgADAAAAEHMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAMBAAMAAAAAAAAAAAAAAQARMSFBkdH/2gAIAQEAAT8QQZQniWNp09kEnyajRYlYNneZ/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Performance panel&quot;
        title=&quot;Performance panel&quot;
        src=&quot;/static/fe328b0c41e424b461dae633750c8226/6a068/performance-01.jpg&quot;
        srcset=&quot;/static/fe328b0c41e424b461dae633750c8226/09b79/performance-01.jpg 240w,
/static/fe328b0c41e424b461dae633750c8226/7cc5e/performance-01.jpg 480w,
/static/fe328b0c41e424b461dae633750c8226/6a068/performance-01.jpg 960w,
/static/fe328b0c41e424b461dae633750c8226/644c5/performance-01.jpg 1440w,
/static/fe328b0c41e424b461dae633750c8226/0f98f/performance-01.jpg 1920w,
/static/fe328b0c41e424b461dae633750c8226/9568a/performance-01.jpg 3024w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Alternatively, you can use the Command menu:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open DevTools.&lt;/li&gt;
&lt;li&gt;Open the Command menu:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;macOS: Command+Shift+P&lt;/li&gt;
&lt;li&gt;Windows, Linux, ChromeOS: Control+Shift+P&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Type &lt;code class=&quot;language-text&quot;&gt;Performance&lt;/code&gt;, select &lt;code class=&quot;language-text&quot;&gt;Show Performance panel&lt;/code&gt;, and press Enter.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c6407175e6553bfd73d2e941d0e69560/9568a/performance-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2gVR/8QAFxABAQEBAAAAAAAAAAAAAAAAAAERAv/aAAgBAQABBQJ0jWo//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAACAh/9oACAEBAAY/AiL/AP/EABkQAQEBAAMAAAAAAAAAAAAAAAERACExQf/aAAgBAQABPyGK6gPS6pz3rsy8qb//2gAMAwEAAgADAAAAEJAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRABAQEBAQEAAAAAAAAAAAAAAREAMUEh/9oACAEBAAE/EKALLh58C9hpy76TmTlIM5kEhk+m/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Performance panel&quot;
        title=&quot;Performance panel&quot;
        src=&quot;/static/c6407175e6553bfd73d2e941d0e69560/6a068/performance-02.jpg&quot;
        srcset=&quot;/static/c6407175e6553bfd73d2e941d0e69560/09b79/performance-02.jpg 240w,
/static/c6407175e6553bfd73d2e941d0e69560/7cc5e/performance-02.jpg 480w,
/static/c6407175e6553bfd73d2e941d0e69560/6a068/performance-02.jpg 960w,
/static/c6407175e6553bfd73d2e941d0e69560/644c5/performance-02.jpg 1440w,
/static/c6407175e6553bfd73d2e941d0e69560/0f98f/performance-02.jpg 1920w,
/static/c6407175e6553bfd73d2e941d0e69560/9568a/performance-02.jpg 3024w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;monitoring-core-web-vitals-live&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#monitoring-core-web-vitals-live&quot; aria-label=&quot;monitoring core web vitals live permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Monitoring Core Web Vitals Live&lt;/h2&gt;
&lt;p&gt;Upon opening the Performance panel, it immediately displays your local &lt;a href=&quot;/blog/web-performance-improve-lcp&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; and &lt;a href=&quot;/blog/web-performance-improve-cls&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; metrics, along with their scores (good, needs improvement, or bad).&lt;/p&gt;
&lt;p&gt;Interacting with your page also captures your local &lt;a href=&quot;/blog/web-performance-improve-inp&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; score, providing a comprehensive overview of your page’s &lt;a href=&quot;/blog/core-web-vitals&quot;&gt;Core Web Vitals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d0d3075d7a7726c02839f24a3cafc073/performance-panel.gif&quot; alt=&quot;Performance panel&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Under the &lt;strong&gt;Interactions&lt;/strong&gt; and &lt;strong&gt;Layout shifts&lt;/strong&gt; tabs, you can find detailed tables of captured interactions and layout shifts, including elements, timings, phases (for interactions), and scores (for layout shifts). To clear these lists, click &lt;strong&gt;Clear&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;comparing-local-metrics-with-user-experience&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparing-local-metrics-with-user-experience&quot; aria-label=&quot;comparing local metrics with user experience permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparing Local Metrics with User Experience&lt;/h3&gt;
&lt;p&gt;You can fetch field data from the Chrome UX Report to compare your site’s user experience with your local metrics.&lt;/p&gt;
&lt;p&gt;To add field data:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In &lt;strong&gt;Performance &gt; Next steps &gt; Field data&lt;/strong&gt;, click &lt;strong&gt;Set up&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/066652fbf2bff8ad8897a6702e00a7ca/768c6/crux-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB21hQf//EABcQAAMBAAAAAAAAAAAAAAAAAAAQETH/2gAIAQEAAQUCMVKv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAERQSH/2gAIAQEAAT8hVsHUEzjB1gj/2gAMAwEAAgADAAAAEFMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhAAAgMBAQAAAAAAAAAAAAAAAAERITFBsf/aAAgBAQABPxBmaXa0va1dYxqfC7BCgPKP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configure field data fetching&quot;
        title=&quot;Configure field data fetching&quot;
        src=&quot;/static/066652fbf2bff8ad8897a6702e00a7ca/6a068/crux-01.jpg&quot;
        srcset=&quot;/static/066652fbf2bff8ad8897a6702e00a7ca/09b79/crux-01.jpg 240w,
/static/066652fbf2bff8ad8897a6702e00a7ca/7cc5e/crux-01.jpg 480w,
/static/066652fbf2bff8ad8897a6702e00a7ca/6a068/crux-01.jpg 960w,
/static/066652fbf2bff8ad8897a6702e00a7ca/644c5/crux-01.jpg 1440w,
/static/066652fbf2bff8ad8897a6702e00a7ca/0f98f/crux-01.jpg 1920w,
/static/066652fbf2bff8ad8897a6702e00a7ca/768c6/crux-01.jpg 3264w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;In the &lt;strong&gt;Configure field data fetching&lt;/strong&gt; dialog, review the &lt;strong&gt;Privacy disclosure&lt;/strong&gt;, and click &lt;strong&gt;Ok&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4102e69ed6c658ed58f9981fdef715bc/768c6/crux-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd1CLJD/xAAYEAACAwAAAAAAAAAAAAAAAAAQEQABQf/aAAgBAQABBQLKDjH/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAADAQEAAAAAAAAAAAAAAAAAAREQcf/aAAgBAQABPyFhqM5xaf/aAAwDAQACAAMAAAAQkM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAwEAAwAAAAAAAAAAAAABABExIXGBsf/aAAgBAQABPxBIk0I46l+Ki7GnPqbz1cNaqf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configure field data fetching&quot;
        title=&quot;Configure field data fetching&quot;
        src=&quot;/static/4102e69ed6c658ed58f9981fdef715bc/6a068/crux-02.jpg&quot;
        srcset=&quot;/static/4102e69ed6c658ed58f9981fdef715bc/09b79/crux-02.jpg 240w,
/static/4102e69ed6c658ed58f9981fdef715bc/7cc5e/crux-02.jpg 480w,
/static/4102e69ed6c658ed58f9981fdef715bc/6a068/crux-02.jpg 960w,
/static/4102e69ed6c658ed58f9981fdef715bc/644c5/crux-02.jpg 1440w,
/static/4102e69ed6c658ed58f9981fdef715bc/0f98f/crux-02.jpg 1920w,
/static/4102e69ed6c658ed58f9981fdef715bc/768c6/crux-02.jpg 3264w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;With field data fetching enabled, the Performance panel compares your local metric scores with those experienced by your users. The collection period is displayed in the Field data section on the right.&lt;/p&gt;
&lt;p&gt;If your website data is not fully available in the Chrome UX Report, you might see limited metrics, such as a CLS score of 0.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0649544e2adc5ac6e287973e8adff045/cecd0/crux-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHuq2kygf/EABcQAQADAAAAAAAAAAAAAAAAABAAEUH/2gAIAQEAAQUClGn/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAACAwEAAAAAAAAAAAAAAAAAAREgITH/2gAIAQEAAT8hyTpio//aAAwDAQACAAMAAAAQ88//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAQQDAAAAAAAAAAAAAAABABEhUZExQbH/2gAIAQEAAT8QSzQ1BPUDiJS5ukPUOWUME//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Field data comparison&quot;
        title=&quot;Field data comparison&quot;
        src=&quot;/static/0649544e2adc5ac6e287973e8adff045/6a068/crux-03.jpg&quot;
        srcset=&quot;/static/0649544e2adc5ac6e287973e8adff045/09b79/crux-03.jpg 240w,
/static/0649544e2adc5ac6e287973e8adff045/7cc5e/crux-03.jpg 480w,
/static/0649544e2adc5ac6e287973e8adff045/6a068/crux-03.jpg 960w,
/static/0649544e2adc5ac6e287973e8adff045/644c5/crux-03.jpg 1440w,
/static/0649544e2adc5ac6e287973e8adff045/0f98f/crux-03.jpg 1920w,
/static/0649544e2adc5ac6e287973e8adff045/cecd0/crux-03.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For other sites like web.dev, you can see a detailed comparison between local metrics and field data:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/90494928792fa85847b7086e1fe3933e/cecd0/crux-04-web-dev.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHs6tRCgf/EABYQAQEBAAAAAAAAAAAAAAAAAAEQAv/aAAgBAQABBQIzVv8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8BWf/EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAQACAgMAAAAAAAAAAAAAAAEAERAhQVGh/9oACAEBAAE/IQrb7AbTqEQdPEMf/9oADAMBAAIAAwAAABADz//EABURAQEAAAAAAAAAAAAAAAAAABEQ/9oACAEDAQE/EAn/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxAh/8QAHRABAAICAgMAAAAAAAAAAAAAAQAhETFBUWGBkf/aAAgBAQABPxCrRc7Xr0xAClXNRKWfcRbRd1zmbvmYOif/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Field data comparison&quot;
        title=&quot;Field data comparison&quot;
        src=&quot;/static/90494928792fa85847b7086e1fe3933e/6a068/crux-04-web-dev.jpg&quot;
        srcset=&quot;/static/90494928792fa85847b7086e1fe3933e/09b79/crux-04-web-dev.jpg 240w,
/static/90494928792fa85847b7086e1fe3933e/7cc5e/crux-04-web-dev.jpg 480w,
/static/90494928792fa85847b7086e1fe3933e/6a068/crux-04-web-dev.jpg 960w,
/static/90494928792fa85847b7086e1fe3933e/644c5/crux-04-web-dev.jpg 1440w,
/static/90494928792fa85847b7086e1fe3933e/0f98f/crux-04-web-dev.jpg 1920w,
/static/90494928792fa85847b7086e1fe3933e/cecd0/crux-04-web-dev.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;configuring-your-environment-to-match-user-conditions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configuring-your-environment-to-match-user-conditions&quot; aria-label=&quot;configuring your environment to match user conditions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Configuring Your Environment to Match User Conditions&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Expand the &lt;strong&gt;Consider your local test conditions&lt;/strong&gt; section.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Typical user environments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Device: 79% mobile, 21% desktop&lt;/li&gt;
&lt;li&gt;Network: 75th percentile similar to Slow 4G throttling&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;In the Environment settings section, set the Network drop-down to &lt;strong&gt;Slow 4G&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reload the page, interact with it to capture your local INP, and compare the metric scores again.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fdb97c618d5b1a0e83e7ae590c536c71/cecd0/crux-05-web-dev.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHs6tRCgf/EABYQAQEBAAAAAAAAAAAAAAAAAAEQAv/aAAgBAQABBQIzVv8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8BWf/EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAQACAgMAAAAAAAAAAAAAAAEAQREhEGGh/9oACAEBAAE/IQxt9gOUKg9RB01C+P/aAAwDAQACAAMAAAAQA8//xAAVEQEBAAAAAAAAAAAAAAAAAAAREP/aAAgBAwEBPxAJ/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8QIf/EABwQAQACAwADAAAAAAAAAAAAAAEAIRExQVGBof/aAAgBAQABPxCrRc7Xr0xBFKu1MhfwRbRd13MIKDcweCf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Field data comparison&quot;
        title=&quot;Field data comparison&quot;
        src=&quot;/static/fdb97c618d5b1a0e83e7ae590c536c71/6a068/crux-05-web-dev.jpg&quot;
        srcset=&quot;/static/fdb97c618d5b1a0e83e7ae590c536c71/09b79/crux-05-web-dev.jpg 240w,
/static/fdb97c618d5b1a0e83e7ae590c536c71/7cc5e/crux-05-web-dev.jpg 480w,
/static/fdb97c618d5b1a0e83e7ae590c536c71/6a068/crux-05-web-dev.jpg 960w,
/static/fdb97c618d5b1a0e83e7ae590c536c71/644c5/crux-05-web-dev.jpg 1440w,
/static/fdb97c618d5b1a0e83e7ae590c536c71/0f98f/crux-05-web-dev.jpg 1920w,
/static/fdb97c618d5b1a0e83e7ae590c536c71/cecd0/crux-05-web-dev.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;improving-core-web-vitals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#improving-core-web-vitals&quot; aria-label=&quot;improving core web vitals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Improving Core Web Vitals&lt;/h3&gt;
&lt;p&gt;Start optimizing your website’s Core Web Vitals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/web-performance-improve-lcp&quot;&gt;Optimize Largest Contentful Paint (LCP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/web-performance-improve-cls&quot;&gt;Optimize Cumulative Layout Shift (CLS)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/web-performance-improve-inp&quot;&gt;Optimize Interaction to Next Paint (INP)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/performance/overview&quot;&gt;Chrome DevTools Performance panel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects]]></title><link>https://trungvose.comerror-9421-too-many-redirects-with-cloudflare-images/</link><guid isPermaLink="false">https://trungvose.comerror-9421-too-many-redirects-with-cloudflare-images/</guid><pubDate>Tue, 29 Oct 2024 14:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern websites are rich with images, videos, and other media, making image optimization crucial for faster load times. Images can be part of your codebase or sourced externally, such as hotel images from suppliers stored on cloud services like AWS S3.&lt;/p&gt;
&lt;p&gt;For images from your design team, request compression before use if possible. However, sometimes you might forget to optimize images before uploading. For external images, third-party services like Cloudflare Images or Cloudinary can help.&lt;/p&gt;
&lt;p&gt;Using Cloudflare Images with my domain trungvose.com, I leveraged two features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Efficient storage and delivery of images with dynamic variants.&lt;/li&gt;
&lt;li&gt;Optimization of externally stored images via transformation requests.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With GatsbyJS and my images deployed on Netlify, I use Cloudflare Images to optimize externally stored images.&lt;/p&gt;
&lt;h2 id=&quot;enable-transformation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enable-transformation&quot; aria-label=&quot;enable transformation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Enable Transformation&lt;/h2&gt;
&lt;p&gt;Transformations let you optimize images stored outside of Cloudflare Images. Cloudflare will automatically cache every transformed image on its global network, so you only need to store the original image at your origin.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the Cloudflare dashboard and select your account.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Images &gt; Transformations&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the domain where you want to enable transformations.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Enable&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After clicking &lt;strong&gt;Enable&lt;/strong&gt;, the button will change to &lt;strong&gt;Disable&lt;/strong&gt;, indicating that transformations are now enabled for your domain.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/92d8474e525e95a573b48636fdc01453/aab53/image-transformation-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe5TCgf/xAAXEAADAQAAAAAAAAAAAAAAAAABECAh/9oACAEBAAEFAoOr/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAIDH/2gAIAQEABj8Cq//EABkQAQEBAAMAAAAAAAAAAAAAAAEREAAhYf/aAAgBAQABPyGN4vug6JTP/9oADAMBAAIAAwAAABCDz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAAMBAQEAAAAAAAAAAAAAAAABETEhcf/aAAgBAQABPxBUOkrlpxqzwTU2mNwSRaR9Fh//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        title=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        src=&quot;/static/92d8474e525e95a573b48636fdc01453/6a068/image-transformation-01.jpg&quot;
        srcset=&quot;/static/92d8474e525e95a573b48636fdc01453/09b79/image-transformation-01.jpg 240w,
/static/92d8474e525e95a573b48636fdc01453/7cc5e/image-transformation-01.jpg 480w,
/static/92d8474e525e95a573b48636fdc01453/6a068/image-transformation-01.jpg 960w,
/static/92d8474e525e95a573b48636fdc01453/644c5/image-transformation-01.jpg 1440w,
/static/92d8474e525e95a573b48636fdc01453/aab53/image-transformation-01.jpg 1570w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;transform-images&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transform-images&quot; aria-label=&quot;transform images permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Transform Images&lt;/h2&gt;
&lt;p&gt;After enabling transformations, you can now transform images using two approaches:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using a &lt;a href=&quot;https://developers.cloudflare.com/images/transform-images/transform-via-url/&quot;&gt;specially-formatted URL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Through &lt;a href=&quot;https://developers.cloudflare.com/images/transform-images/transform-via-workers/&quot;&gt;Cloudflare Workers&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;supported-input-formats&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#supported-input-formats&quot; aria-label=&quot;supported input formats permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Supported Input Formats&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JPEG&lt;/li&gt;
&lt;li&gt;PNG&lt;/li&gt;
&lt;li&gt;GIF (including animations)&lt;/li&gt;
&lt;li&gt;WebP (including animations)&lt;/li&gt;
&lt;li&gt;SVG&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;using-a-specially-formatted-url&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-a-specially-formatted-url&quot; aria-label=&quot;using a specially formatted url permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using a Specially-Formatted URL&lt;/h2&gt;
&lt;p&gt;To get started, I’ll use a specially-formatted URL. You can read more &lt;a href=&quot;https://developers.cloudflare.com/images/transform-images/transform-via-url/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can convert and resize images by requesting them via a specially-formatted URL. This way, you do not need to write any code, only change the HTML markup of your website to use the new URLs. The format is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://&amp;lt;ZONE&gt;/cdn-cgi/image/&amp;lt;OPTIONS&gt;/&amp;lt;SOURCE-IMAGE&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;ZONE&gt;&lt;/code&gt;: Your domain, e.g., trungvose.com.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/cdn-cgi/image/&lt;/code&gt;: A fixed prefix that identifies this as a special path handled by Cloudflare’s built-in Worker. This path will work when you have enabled transformations for your domain.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;OPTIONS&gt;&lt;/code&gt;: A comma-separated list of options such as width, height, and quality.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;SOURCE-IMAGE&gt;&lt;/code&gt;: The URL of the original image.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example&quot; aria-label=&quot;example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example&lt;/h3&gt;
&lt;p&gt;I have this image inside my source code, uploaded to Netlify:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://trungvose.com/img/speaking/2024-10-19-javascript-bangkok-01.jpg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Applying the above, the optimized image URL with a width of 1280px would be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://trungvose.com/cdn-cgi/image/format=auto,width=1280/img/speaking/2024-10-19-javascript-bangkok-01.jpg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;error-9421-too-many-redirects&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#error-9421-too-many-redirects&quot; aria-label=&quot;error 9421 too many redirects permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Error 9421: Too Many Redirects&lt;/h2&gt;
&lt;p&gt;After opening the above image URL in the browser, I encountered this error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error 9421: Too many redirects&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cbf200d65240af68676ae0e84241fd78/d34ab/image-transformation-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHrNhC4/8QAGxAAAQQDAAAAAAAAAAAAAAAAAAECAxEQExT/2gAIAQEAAQUC0RnPGNajEsvH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAQUBAAAAAAAAAAAAAAAAAAIRIDGhMv/aAAgBAQAGPwKtOdGTD//EABsQAAMAAgMAAAAAAAAAAAAAAAABESFhcZHw/9oACAEBAAE/Id/seTE4RclFCyj/2gAMAwEAAgADAAAAEPMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBAAAgICAwAAAAAAAAAAAAAAAAERYSHwMVGB/9oACAEBAAE/EJFl2/Za3sYnZ5iTz6WFhzGf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        title=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        src=&quot;/static/cbf200d65240af68676ae0e84241fd78/6a068/image-transformation-02.jpg&quot;
        srcset=&quot;/static/cbf200d65240af68676ae0e84241fd78/09b79/image-transformation-02.jpg 240w,
/static/cbf200d65240af68676ae0e84241fd78/7cc5e/image-transformation-02.jpg 480w,
/static/cbf200d65240af68676ae0e84241fd78/6a068/image-transformation-02.jpg 960w,
/static/cbf200d65240af68676ae0e84241fd78/644c5/image-transformation-02.jpg 1440w,
/static/cbf200d65240af68676ae0e84241fd78/0f98f/image-transformation-02.jpg 1920w,
/static/cbf200d65240af68676ae0e84241fd78/d34ab/image-transformation-02.jpg 2784w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Searching Google, I found a Cloudflare community post that explains the issue.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://community.cloudflare.com/t/pages-images-error-9421-too-many-redirects/723200/1&quot;&gt;Cloudflare Community Post: Pages&amp;#x26;Images: ERROR 9421: Too many redirects&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The post mentioned:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If your site’s SSL/TLS mode is set to “Flexible”, change it to “Full (strict)”. Pages does not work with plain HTTP traffic, only HTTPS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I changed the SSL/TLS mode to &lt;strong&gt;Full (strict)&lt;/strong&gt;, and the issue was resolved.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/d34ab/image-transformation-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdxxQxh//8QAGBABAAMBAAAAAAAAAAAAAAAAAAEQESH/2gAIAQEAAQUCcvUP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAERIVFh/9oACAEBAAE/IYhqKxcK9lGSP//aAAwDAQACAAMAAAAQYw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAACAgIDAAAAAAAAAAAAAAABEQAxQVFxkbH/2gAIAQEAAT8QQASM24AghtxFpiCjwJwdSwZ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        title=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        src=&quot;/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/6a068/image-transformation-03.jpg&quot;
        srcset=&quot;/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/09b79/image-transformation-03.jpg 240w,
/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/7cc5e/image-transformation-03.jpg 480w,
/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/6a068/image-transformation-03.jpg 960w,
/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/644c5/image-transformation-03.jpg 1440w,
/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/0f98f/image-transformation-03.jpg 1920w,
/static/e0cb5ae3b3d6c8f8e873d2067c305b8b/d34ab/image-transformation-03.jpg 2784w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e345a01ca86e85dce537fdf00a95199c/768c6/image-transformation-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHbqWjEL//EABcQAQEBAQAAAAAAAAAAAAAAAAAxEBH/2gAIAQEAAQUCXeq//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhABAQEAAwEAAAAAAAAAAAAAAQARITFBYf/aAAgBAQABPyFwgI8S/Ooz5bP/2gAMAwEAAgADAAAAEKzP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQARMUEhobH/2gAIAQEAAT8QdCmbhFgSjryC0NuW4Ao7zZjU/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        title=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        src=&quot;/static/e345a01ca86e85dce537fdf00a95199c/6a068/image-transformation-04.jpg&quot;
        srcset=&quot;/static/e345a01ca86e85dce537fdf00a95199c/09b79/image-transformation-04.jpg 240w,
/static/e345a01ca86e85dce537fdf00a95199c/7cc5e/image-transformation-04.jpg 480w,
/static/e345a01ca86e85dce537fdf00a95199c/6a068/image-transformation-04.jpg 960w,
/static/e345a01ca86e85dce537fdf00a95199c/644c5/image-transformation-04.jpg 1440w,
/static/e345a01ca86e85dce537fdf00a95199c/0f98f/image-transformation-04.jpg 1920w,
/static/e345a01ca86e85dce537fdf00a95199c/768c6/image-transformation-04.jpg 3264w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;result-and-comparison&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#result-and-comparison&quot; aria-label=&quot;result and comparison permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Result and Comparison&lt;/h2&gt;
&lt;h3 id=&quot;before&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#before&quot; aria-label=&quot;before permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Before&lt;/h3&gt;
&lt;p&gt;The original image:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://trungvose.com/img/speaking/2024-10-19-javascript-bangkok-01.jpg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Size: 3.8MB&lt;/li&gt;
&lt;li&gt;Dimensions: 4032 x 3024&lt;/li&gt;
&lt;li&gt;Format: image/jpeg&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/19bbeef7fae686b945ce03a24c43d7b4/81fa7/image-transformation-05-before.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEEAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHGbppEWg//xAAaEAACAwEBAAAAAAAAAAAAAAABAgMREgAE/9oACAEBAAEFAikl1JXn1go2zG/RoQv/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAbEAACAgMBAAAAAAAAAAAAAAAAAREyIkFRov/aAAgBAQAGPwKZ9DeQ56O5Rmz/xAAcEAACAwEAAwAAAAAAAAAAAAABEQAhQTFRYaH/2gAIAQEAAT8hPQwZ+pYCg7coHfkfQhKAhOciPUapeNaJ/9oADAMBAAIAAwAAABBnD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAgEBPxC4r//EAB0QAQACAwEAAwAAAAAAAAAAAAERIQAxQXFRgaH/2gAIAQEAAT8QdKlQERK44mCLaUHfnWIsRQsX7MiYo0Q/uvMEoSqyE5Xax4CTK2dB3zP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        title=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        src=&quot;/static/19bbeef7fae686b945ce03a24c43d7b4/6a068/image-transformation-05-before.jpg&quot;
        srcset=&quot;/static/19bbeef7fae686b945ce03a24c43d7b4/09b79/image-transformation-05-before.jpg 240w,
/static/19bbeef7fae686b945ce03a24c43d7b4/7cc5e/image-transformation-05-before.jpg 480w,
/static/19bbeef7fae686b945ce03a24c43d7b4/6a068/image-transformation-05-before.jpg 960w,
/static/19bbeef7fae686b945ce03a24c43d7b4/644c5/image-transformation-05-before.jpg 1440w,
/static/19bbeef7fae686b945ce03a24c43d7b4/0f98f/image-transformation-05-before.jpg 1920w,
/static/19bbeef7fae686b945ce03a24c43d7b4/81fa7/image-transformation-05-before.jpg 2284w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;after&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#after&quot; aria-label=&quot;after permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;After&lt;/h3&gt;
&lt;p&gt;The optimized image:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://trungvose.com/cdn-cgi/image/format=auto,width=1280/img/speaking/2024-10-19-javascript-bangkok-01.jpg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Size: 243KB&lt;/li&gt;
&lt;li&gt;Dimensions: 1280 x 960&lt;/li&gt;
&lt;li&gt;Format: image/avif&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3c3ae4e2680fabd82451019394c2f2b7/ab663/image-transformation-05-after.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 83.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAARABQDASIAAhEBAxEB/8QAGQABAQADAQAAAAAAAAAAAAAAAAQBAgMF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAZ+HpYSlQHaoKBn/xAAcEAACAgIDAAAAAAAAAAAAAAACEQEDABIEEBP/2gAIAQEAAQUC0tSsXDfloTkDykZgO//EABURAQEAAAAAAAAAAAAAAAAAABAB/9oACAEDAQE/ASn/xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAgEBPwEh/8QAHhAAAQIHAQAAAAAAAAAAAAAAABGBAQIgIjJCUVL/2gAIAQEABj8Cb0LcRXpuYzD0f//EABwQAAICAwEBAAAAAAAAAAAAAAERACExQVFxEP/aAAgBAQABPyGoWSOIQZFHdmbSxv4IC8/CokqjlNQybZaxNGDA+f/aAAwDAQACAAMAAAAQR8BA/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPxAf/8QAFREBAQAAAAAAAAAAAAAAAAAAASD/2gAIAQIBAT8QUh//xAAeEAEAAwACAgMAAAAAAAAAAAABABEhQaFRYTFxgf/aAAgBAQABPxBKnAqzM8PMQPiiwv3vsiqttNK/pGxVm1V3mgTCpfaq3iEFyLIPwedj0TrRn//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        title=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        src=&quot;/static/3c3ae4e2680fabd82451019394c2f2b7/6a068/image-transformation-05-after.jpg&quot;
        srcset=&quot;/static/3c3ae4e2680fabd82451019394c2f2b7/09b79/image-transformation-05-after.jpg 240w,
/static/3c3ae4e2680fabd82451019394c2f2b7/7cc5e/image-transformation-05-after.jpg 480w,
/static/3c3ae4e2680fabd82451019394c2f2b7/6a068/image-transformation-05-after.jpg 960w,
/static/3c3ae4e2680fabd82451019394c2f2b7/644c5/image-transformation-05-after.jpg 1440w,
/static/3c3ae4e2680fabd82451019394c2f2b7/0f98f/image-transformation-05-after.jpg 1920w,
/static/3c3ae4e2680fabd82451019394c2f2b7/ab663/image-transformation-05-after.jpg 2232w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;cloudflare-dashboard&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cloudflare-dashboard&quot; aria-label=&quot;cloudflare dashboard permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cloudflare Dashboard&lt;/h3&gt;
&lt;p&gt;In the Cloudflare dashboard, you can view detailed metrics for transformed images, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bandwidth Saved&lt;/strong&gt;: The amount of data saved by using optimized images.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Original Size&lt;/strong&gt;: The size of the image before transformation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resized&lt;/strong&gt;: The size of the image after transformation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d79aa186bbab3c717f1bfc33a6c70959/bbc43/image-transformation-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAe5YXSA//8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEQESEi/9oACAEBAAEFArZbjTo0/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhABAQEAAAAAAAAAAAAAAAAAMRAA/9oACAEBAAY/AjFZ/8QAHxAAAgECBwAAAAAAAAAAAAAAAAERITFBUXGBofDx/9oACAEBAAE/Iajr1OlinKCJ3jYjwEsT4P/aAAwDAQACAAMAAAAQg9//xAAVEQEBAAAAAAAAAAAAAAAAAAAQIf/aAAgBAwEBPxCH/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQIBAT8Qh//EAB4QAQACAQQDAAAAAAAAAAAAAAEAEWEhQVFxkcHw/9oACAEBAAE/ECgGp3APUy/CJFvRFVRHFH3MR+5lDQWP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        title=&quot;Configuring Cloudflare Images and fixing ERROR 9421: Too many redirects&quot;
        src=&quot;/static/d79aa186bbab3c717f1bfc33a6c70959/6a068/image-transformation-06.jpg&quot;
        srcset=&quot;/static/d79aa186bbab3c717f1bfc33a6c70959/09b79/image-transformation-06.jpg 240w,
/static/d79aa186bbab3c717f1bfc33a6c70959/7cc5e/image-transformation-06.jpg 480w,
/static/d79aa186bbab3c717f1bfc33a6c70959/6a068/image-transformation-06.jpg 960w,
/static/d79aa186bbab3c717f1bfc33a6c70959/644c5/image-transformation-06.jpg 1440w,
/static/d79aa186bbab3c717f1bfc33a6c70959/0f98f/image-transformation-06.jpg 1920w,
/static/d79aa186bbab3c717f1bfc33a6c70959/bbc43/image-transformation-06.jpg 2350w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Cloudflare Images is a powerful tool to optimize images stored outside of Cloudflare. By enabling transformations, you can resize, convert, and optimize images on the fly. If you encounter ERROR 9421: Too many redirects, check your SSL/TLS mode and ensure it is set to &lt;strong&gt;Full (strict)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;While URL transformation is straightforward, it lacks the flexibility of Cloudflare &lt;a href=&quot;https://developers.cloudflare.com/images/transform-images/transform-via-workers/&quot;&gt;Workers&lt;/a&gt;, which allow for custom image transformations. In a future article, I’ll explore using Cloudflare Workers. Here are some advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Custom URL schemes&lt;/strong&gt;: Use preset names like &lt;code class=&quot;language-text&quot;&gt;thumbnail&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;large&lt;/code&gt; instead of pixel dimensions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hide original image locations&lt;/strong&gt;: Store images in an external S3 bucket or a hidden server folder without exposing URLs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content negotiation&lt;/strong&gt;: Dynamically adapt image sizes, formats, and quality based on device and network conditions.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications]]></title><link>https://trungvose.comgmail-filter-calendar-invite/</link><guid isPermaLink="false">https://trungvose.comgmail-filter-calendar-invite/</guid><pubDate>Wed, 25 Sep 2024 02:10:00 GMT</pubDate><content:encoded>&lt;p&gt;We don’t need to receive notifications when attendees &lt;code class=&quot;language-text&quot;&gt;Accepted&lt;/code&gt; invites. It’s more useful to be notified if they &lt;code class=&quot;language-text&quot;&gt;Decline&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Tentatively Accept&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;Propose a new time&lt;/code&gt;. With this filter, all Calendar notifications will be tagged with a &lt;code class=&quot;language-text&quot;&gt;Calendar&lt;/code&gt; label, and &lt;code class=&quot;language-text&quot;&gt;Accepted&lt;/code&gt; notifications will automatically be archived, keeping your inbox cleaner.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/63adf2da2c3f20099747cff22f02f750/29007/gmail-filter-calendar-invite-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 19.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAwklEQVQI1z3GaU7DMBBA4dz/UmwCpZDCj9puiWfSUC/xMvbY0AOgCgnp6dMbrJROKqfUTaGskEbIy0F8CQEHcZTqeJq1RlxWwPN/J0ClcWjEnVsOIfothRC3jWLiUnOMlSinRESZKGciolJqzsTMP9frJy5DDiml5K1xxnhrnTGU8jdzK6VX5lpb49Ya882/6b0zM5zXwX+8m2nvpr17nexusuObedldHp/X+ye4e5DjKKWYQSMCLogLAsKsQcNirP8FJ7nTqPwwIUsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        title=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        src=&quot;/static/63adf2da2c3f20099747cff22f02f750/d9199/gmail-filter-calendar-invite-01.png&quot;
        srcset=&quot;/static/63adf2da2c3f20099747cff22f02f750/8ff5a/gmail-filter-calendar-invite-01.png 240w,
/static/63adf2da2c3f20099747cff22f02f750/e85cb/gmail-filter-calendar-invite-01.png 480w,
/static/63adf2da2c3f20099747cff22f02f750/d9199/gmail-filter-calendar-invite-01.png 960w,
/static/63adf2da2c3f20099747cff22f02f750/07a9c/gmail-filter-calendar-invite-01.png 1440w,
/static/63adf2da2c3f20099747cff22f02f750/29007/gmail-filter-calendar-invite-01.png 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;how-to-set-it-up&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-set-it-up&quot; aria-label=&quot;how to set it up permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to set it up:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;In the search bar, paste this:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;filename:invite.ics AND &quot;accepted&quot; -{&quot;tentatively&quot; OR &quot;proposed&quot; OR &quot;appointment booked&quot; OR &quot;updated invitation&quot;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1fc6276eea6822be87bf16338d741388/0047d/gmail-filter-calendar-invite-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.74999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB3FesYgs//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhABAAEFAAAAAAAAAAAAAAAAAQAQEVFhgf/aAAgBAQABPyGxicIOqk//2gAMAwEAAgADAAAAEIgf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGBABAQEBAQAAAAAAAAAAAAAAAQARMSH/2gAIAQEAAT8QM+LR2sTwRyHsNL//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        title=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        src=&quot;/static/1fc6276eea6822be87bf16338d741388/6a068/gmail-filter-calendar-invite-02.jpg&quot;
        srcset=&quot;/static/1fc6276eea6822be87bf16338d741388/09b79/gmail-filter-calendar-invite-02.jpg 240w,
/static/1fc6276eea6822be87bf16338d741388/7cc5e/gmail-filter-calendar-invite-02.jpg 480w,
/static/1fc6276eea6822be87bf16338d741388/6a068/gmail-filter-calendar-invite-02.jpg 960w,
/static/1fc6276eea6822be87bf16338d741388/644c5/gmail-filter-calendar-invite-02.jpg 1440w,
/static/1fc6276eea6822be87bf16338d741388/0047d/gmail-filter-calendar-invite-02.jpg 1620w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Click the &lt;strong&gt;Show search options&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;When the panel open, click &lt;strong&gt;Search&lt;/strong&gt; button&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b920bfe4f6c134729cb9f129ee8922d1/591c3/gmail-filter-calendar-invite-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHvFIyT/8QAFhABAQEAAAAAAAAAAAAAAAAAAQAg/9oACAEBAAEFAsjf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhAAAgIDAAAAAAAAAAAAAAAAEVEAASAhQf/aAAgBAQABPyEUoKWBDrrn/9oADAMBAAIAAwAAABAjP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EFf/xAAVEQEBAAAAAAAAAAAAAAAAAAABAP/aAAgBAgEBPxCRL//EABkQAQADAQEAAAAAAAAAAAAAAAEAESExEP/aAAgBAQABPxDwE5yoFO7FzIuxKRlrn//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        title=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        src=&quot;/static/b920bfe4f6c134729cb9f129ee8922d1/6a068/gmail-filter-calendar-invite-03.jpg&quot;
        srcset=&quot;/static/b920bfe4f6c134729cb9f129ee8922d1/09b79/gmail-filter-calendar-invite-03.jpg 240w,
/static/b920bfe4f6c134729cb9f129ee8922d1/7cc5e/gmail-filter-calendar-invite-03.jpg 480w,
/static/b920bfe4f6c134729cb9f129ee8922d1/6a068/gmail-filter-calendar-invite-03.jpg 960w,
/static/b920bfe4f6c134729cb9f129ee8922d1/644c5/gmail-filter-calendar-invite-03.jpg 1440w,
/static/b920bfe4f6c134729cb9f129ee8922d1/591c3/gmail-filter-calendar-invite-03.jpg 1610w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Set the following options:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Skip the Inbox (Archive it)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mark as read&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apply the label&lt;/strong&gt;: Create a label called “Calendar”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apply the filter to all matching conversations&lt;/strong&gt; to clean up previous emails as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then click Create filter&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1e35f63d2dc852e8c4787a6147089ac7/3b2a1/gmail-filter-calendar-invite-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHt2kxKz//EABgQAAMBAQAAAAAAAAAAAAAAAAAQEQEC/9oACAEBAAEFAoQq62L/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAABECD/2gAIAQEABj8CgL//xAAaEAACAgMAAAAAAAAAAAAAAAAAARFhEEFR/9oACAEBAAE/IVQhwkJWPgW3j//aAAwDAQACAAMAAAAQyN//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxBZ/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/EA2f/8QAGxABAAICAwAAAAAAAAAAAAAAAQARIZExYXH/2gAIAQEAAT8QZcjRKSwXPLUfm0RAOJuX1P/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        title=&quot;Sharing my go-to Gmail filter to clean up unnecessary Calendar notifications&quot;
        src=&quot;/static/1e35f63d2dc852e8c4787a6147089ac7/6a068/gmail-filter-calendar-invite-04.jpg&quot;
        srcset=&quot;/static/1e35f63d2dc852e8c4787a6147089ac7/09b79/gmail-filter-calendar-invite-04.jpg 240w,
/static/1e35f63d2dc852e8c4787a6147089ac7/7cc5e/gmail-filter-calendar-invite-04.jpg 480w,
/static/1e35f63d2dc852e8c4787a6147089ac7/6a068/gmail-filter-calendar-invite-04.jpg 960w,
/static/1e35f63d2dc852e8c4787a6147089ac7/644c5/gmail-filter-calendar-invite-04.jpg 1440w,
/static/1e35f63d2dc852e8c4787a6147089ac7/0f98f/gmail-filter-calendar-invite-04.jpg 1920w,
/static/1e35f63d2dc852e8c4787a6147089ac7/3b2a1/gmail-filter-calendar-invite-04.jpg 2084w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, all &lt;code class=&quot;language-text&quot;&gt;Accepted&lt;/code&gt; calendar notifications will be automatically archived so they won’t clutter your inbox.&lt;/p&gt;
&lt;h3 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h3&gt;
&lt;p&gt;Day 1: Essential Gmail Filters for Focus by Jeff Su&lt;/p&gt;</content:encoded></item><item><title><![CDATA[TypeScript is Operator for Type Narrowing]]></title><link>https://trungvose.comtypescript-is-operator-type-narrowing/</link><guid isPermaLink="false">https://trungvose.comtypescript-is-operator-type-narrowing/</guid><pubDate>Sun, 22 Sep 2024 08:30:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;TypeScript’s Control Flow Analysis is generally effective at narrowing types. For instance, starting from &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html&quot;&gt;TypeScript 4.4&lt;/a&gt;, type guards can influence control flow more effectively. Consider the following example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// In TS 4.3 and below&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; argIsString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; arg &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;argIsString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//              ~~~~~~~~~~~&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Error! Property &apos;toUpperCase&apos; does not exist on type &apos;unknown&apos;.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In earlier versions of TypeScript, even though &lt;code class=&quot;language-text&quot;&gt;argIsString&lt;/code&gt; is assigned the result of a type guard (&lt;code class=&quot;language-text&quot;&gt;typeof arg === &quot;string&quot;&lt;/code&gt;), TypeScript would lose that information, resulting in an error. In TypeScript 4.4 and above, the control flow is improved, and this issue is resolved:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// In TS 4.4 and above&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; argIsString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; arg &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;argIsString&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// No error, TypeScript knows arg is a string now.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, there are still situations where TypeScript’s type inference falls short. For example, when filtering an array that contains &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;, or other falsy values, TypeScript can’t determine whether the filtered array is completely truthy.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fruits &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;apple&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;banana&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cherry&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (string | undefined)[]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; filteredFruits &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fruits&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (string | undefined)[]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Even though the &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; values have been filtered out, TypeScript still thinks the result is of type &lt;code class=&quot;language-text&quot;&gt;(string | undefined)[]&lt;/code&gt;, instead of &lt;code class=&quot;language-text&quot;&gt;string[]&lt;/code&gt;. This can lead to type issues down the line, requiring manual type assertions or dealing with incorrect type assumptions.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2ecce2aa30a398edaee82bbb05f0a091/211f9/ts-is-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABygSD/8QAFRABAQAAAAAAAAAAAAAAAAAAABH/2gAIAQEAAQUCqq//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAYEAACAwAAAAAAAAAAAAAAAAAAARBRcf/aAAgBAQABPyFXZqH/2gAMAwEAAgADAAAAEAAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGBABAQEBAQAAAAAAAAAAAAAAAREAIZH/2gAIAQEAAT8QSNfMl7S5V47/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;TypeScript is Operator for Type Narrowing&quot;
        title=&quot;TypeScript is Operator for Type Narrowing&quot;
        src=&quot;/static/2ecce2aa30a398edaee82bbb05f0a091/6a068/ts-is-01.jpg&quot;
        srcset=&quot;/static/2ecce2aa30a398edaee82bbb05f0a091/09b79/ts-is-01.jpg 240w,
/static/2ecce2aa30a398edaee82bbb05f0a091/7cc5e/ts-is-01.jpg 480w,
/static/2ecce2aa30a398edaee82bbb05f0a091/6a068/ts-is-01.jpg 960w,
/static/2ecce2aa30a398edaee82bbb05f0a091/644c5/ts-is-01.jpg 1440w,
/static/2ecce2aa30a398edaee82bbb05f0a091/211f9/ts-is-01.jpg 1799w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;To resolve this, you can use a custom type guard with TypeScript’s &lt;code class=&quot;language-text&quot;&gt;is&lt;/code&gt; operator, which explicitly informs TypeScript of the correct type. Here’s how to create an &lt;code class=&quot;language-text&quot;&gt;isTruthyPredicate&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;isTruthyPredicate&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SomeType&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  item&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SomeType &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; SomeType &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This predicate ensures that TypeScript understands when an item is truthy. By using this function in a filter operation, you can ensure TypeScript infers the correct type:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; filteredFruitsOnlyTruthy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fruits&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isTruthyPredicate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// string[]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, &lt;code class=&quot;language-text&quot;&gt;filteredFruitsOnlyTruthy&lt;/code&gt; is correctly inferred as &lt;code class=&quot;language-text&quot;&gt;string[]&lt;/code&gt;, eliminating the need for type assertions and making the code safer.&lt;/p&gt;
&lt;h3 id=&quot;why-use-the-is-operator-for-type-narrowing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-use-the-is-operator-for-type-narrowing&quot; aria-label=&quot;why use the is operator for type narrowing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why use the is Operator for Type Narrowing?&lt;/h3&gt;
&lt;p&gt;When searching, I found this answer is sensible too.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/q/51124837/3375906&quot;&gt;https://stackoverflow.com/q/51124837/3375906&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In TypeScript, a &lt;code class=&quot;language-text&quot;&gt;boolean&lt;/code&gt; is just a data type that can be either &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;, whereas the &lt;code class=&quot;language-text&quot;&gt;is&lt;/code&gt; operator is specifically used for type-testing. It tells TypeScript that a particular condition not only checks a value but also narrows down its type. Let’s take a look at an example to understand the difference:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Species&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cat&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dog&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    species&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Species&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; species&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Species &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;meow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Meow&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;petIsCat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pet&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pet &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Cat &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; pet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;species &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;petIsCatBoolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pet&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; pet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;species &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; p&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;meow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ERROR: Property &apos;meow&apos; does not exist on type &apos;Pet&apos;.&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;petIsCat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;meow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Now the compiler knows for sure that &apos;p&apos; is of type Cat, and it has a &apos;meow&apos; method&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;petIsCatBoolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;meow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ERROR: Property &apos;meow&apos; does not exist on type &apos;Pet&apos;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;petIsCat&lt;/code&gt;&lt;/strong&gt; is a custom type guard using the &lt;code class=&quot;language-text&quot;&gt;is&lt;/code&gt; operator. It tells TypeScript that if the condition is true, the &lt;code class=&quot;language-text&quot;&gt;pet&lt;/code&gt; is of type &lt;code class=&quot;language-text&quot;&gt;Cat&lt;/code&gt;, and thus, the &lt;code class=&quot;language-text&quot;&gt;meow&lt;/code&gt; method is available.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;petIsCatBoolean&lt;/code&gt;&lt;/strong&gt; only returns a boolean, and while it performs the same check, it doesn’t narrow the type. As a result, TypeScript doesn’t know that &lt;code class=&quot;language-text&quot;&gt;p&lt;/code&gt; is a &lt;code class=&quot;language-text&quot;&gt;Cat&lt;/code&gt; and throws an error when trying to access &lt;code class=&quot;language-text&quot;&gt;meow&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;playground-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#playground-example&quot; aria-label=&quot;playground example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Playground Example&lt;/h3&gt;
&lt;p&gt;You can see the full example in the TypeScript playground &lt;a href=&quot;https://www.typescriptlang.org/play/?#code/MYewdgzgLgBAZgJwK4EsoRgXhgbQOQCGADkQDYCmeANDEmACblwpjn014BGBYPB1tBkxZsOwABbkECAJ54AugG4YAehUwAFNAQsA5jAA+gxs1b0AlDnkAoa6EixmpKFLYAxZGgzZEqdADonFwQNNHIAWywAPhgAQliw8PNlNU1tPUNjYTNLG2tyAA8iEARHOmAoFHAYFAgAFWQocRkABQQ2FGACFwAeAGUQcPI6mSJyKI1rGBqXcIAuGAGhkbHMzhAQCh5MsCRSUky6ExF2a3MFxJqMJeHR8hgAbymYdqgkBDA4hNnFawBfWz2aDwFDOVz0Dx+CAAeTApBkDSQTRkWHgngCQSkoXqjWabQ6XRcyVU6nSYF0VmsQA&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;While TypeScript’s Control Flow Analysis has improved over the years, as seen in features introduced in TypeScript 4.4, there are still cases, like filtering out falsy values, where the type inference doesn’t work as expected. In such situations, custom type predicates using the &lt;code class=&quot;language-text&quot;&gt;is&lt;/code&gt; operator can be a clean and effective solution to ensure TypeScript narrows types properly, improving both safety and developer experience.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[nvm keeps "forgetting" node version in new VSCode terminal sessions]]></title><link>https://trungvose.comnvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh/</link><guid isPermaLink="false">https://trungvose.comnvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh/</guid><pubDate>Tue, 10 Sep 2024 15:50:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;In my project, we define engines in &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;engines&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&gt;=20.10.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;pnpm&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;9.9.0&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Recently, when I run a pnpm command, I encountered this error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Your Node version is incompatible with &quot;/Users/trung.vo/project-a&quot;.

Expected version: &gt;=20.10.0
Got: v18.19.1

This is happening because the package&apos;s manifest has an engines.node field specified.
To fix this issue, install the required Node version.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I checked the Node version on my machine:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt;
v18.19.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I then installed the correct version of Node using nvm and set the default version to 20.10.0:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;nvm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20.10&lt;/span&gt;.0
nvm &lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; default &lt;span class=&quot;token number&quot;&gt;20.10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, when I opened a new terminal session in VSCode, the Node version reverted to &lt;code class=&quot;language-text&quot;&gt;18.19.1&lt;/code&gt;. See the following gif for more details:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/80b2918024c8bd3d890a15a371600d0a/01-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.gif&quot; alt=&quot;nvm keeps &amp;#x22;forgetting&amp;#x22; node version in new VSCode terminal sessions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;After exploring multiple Stack Overflow questions, I finally found a working solution.&lt;/p&gt;
&lt;p&gt;You need to add the following line at the TOP of your &lt;code class=&quot;language-text&quot;&gt;.bashrc&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; file, before anything else:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/usr/local/bin:&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;getconf &lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/81dd15626ccf356ad3c8dddb97318d65/8ec0a/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 78.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHKKpIX/8QAGRABAAIDAAAAAAAAAAAAAAAAAAERECEi/9oACAEBAAEFAsW6VLb/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAEAAwEAAAAAAAAAAAAAAAAAATFBEP/aAAgBAQAGPwJfNVLX/8QAGhAAAgIDAAAAAAAAAAAAAAAAABEBUSEx8f/aAAgBAQABPyFy9jmw1k5DiCqP/9oADAMBAAIAAwAAABCcL//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAwEBPxCrj//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAgEBPxCJr//EABoQAQADAAMAAAAAAAAAAAAAAAEAESExQXH/2gAIAQEAAT8QUQo1jwQRavCEBW+hizaj6itDgUY5P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nvm keeps &amp;quot;forgetting&amp;quot; node version in new VSCode terminal sessions&quot;
        title=&quot;nvm keeps &amp;quot;forgetting&amp;quot; node version in new VSCode terminal sessions&quot;
        src=&quot;/static/81dd15626ccf356ad3c8dddb97318d65/6a068/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg&quot;
        srcset=&quot;/static/81dd15626ccf356ad3c8dddb97318d65/09b79/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg 240w,
/static/81dd15626ccf356ad3c8dddb97318d65/7cc5e/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg 480w,
/static/81dd15626ccf356ad3c8dddb97318d65/6a068/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg 960w,
/static/81dd15626ccf356ad3c8dddb97318d65/644c5/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg 1440w,
/static/81dd15626ccf356ad3c8dddb97318d65/0f98f/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg 1920w,
/static/81dd15626ccf356ad3c8dddb97318d65/8ec0a/02-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.jpg 2560w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In my case, I am using oh-my-zsh and the VSCode integrated terminal. Adding the line above to my &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; file resolved the issue.&lt;/p&gt;
&lt;p&gt;The reason, as explained in the answers below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/55547261/3375906&quot;&gt;https://stackoverflow.com/a/55547261/3375906&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/50011895/3375906&quot;&gt;https://stackoverflow.com/a/50011895/3375906&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apparently, nvm does not get along with integrated terminals or subshells in these editors. When loading them, the environment variable &lt;code class=&quot;language-text&quot;&gt;$PATH&lt;/code&gt; is modified internally. According to a comment by one of the contributors of this package in the issue reported &lt;a href=&quot;https://github.com/nvm-sh/nvm/issues/1652&quot;&gt;NVM fails to load within nested shell #1652&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;@charsleysa I know why nvm is throwing this error. In your subshell, somehow the /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin part of your PATH has been moved from the end of the PATH to the start.
When nvm is then started, it calls nvm_change_path (my contribution changed it to this from nvm_prepend_path), which modifies the nvm-relevant part of the path in place.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After adding the line above to my &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; file, I ran &lt;code class=&quot;language-text&quot;&gt;nvm alias default 20.10&lt;/code&gt; again. Upon opening a new terminal session in VSCode, the Node version was correctly set to &lt;code class=&quot;language-text&quot;&gt;20.10.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/9613378827d99e5877277c24d2b7d925/03-nvm-keeps-forgetting-node-version-in-new-vs-code-terminal-sessions-vscode-oh-my-zsh.gif&quot; alt=&quot;nvm keeps &amp;#x22;forgetting&amp;#x22; node version in new VSCode terminal sessions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Improve Interaction to Next Paint (INP) ]]></title><description><![CDATA[In this article, we will explore how to improve Interaction to Next Paint (INP)]]></description><link>https://trungvose.comweb-performance-improve-inp/</link><guid isPermaLink="false">https://trungvose.comweb-performance-improve-inp/</guid><pubDate>Wed, 14 Aug 2024 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Imagine you’re at a restaurant. You place your order, and the waiter immediately acknowledges it with a nod or a smile. This quick response reassures you that your order is being processed. Now, think about a situation where the waiter ignores you for a while before acknowledging your order. You might feel frustrated or think that your order wasn’t taken. This is similar to how web pages should respond to user interactions.&lt;/p&gt;
&lt;h2 id=&quot;understanding-inp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#understanding-inp&quot; aria-label=&quot;understanding inp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Understanding INP&lt;/h2&gt;
&lt;p&gt;Chrome usage data shows that 90% of a user’s time on a page is spent after it loads. Thus, careful measurement of &lt;strong&gt;responsiveness&lt;/strong&gt; throughout the page lifecycle is important. This is what the INP metric assesses.&lt;/p&gt;
&lt;p&gt;Good responsiveness means that a page responds quickly to interactions. When a page responds to an interaction, the browser presents &lt;em&gt;visual feedback&lt;/em&gt; in the next frame that it paints. Visual feedback tells you if, for example, an item you add to an online shopping cart is indeed being added, whether a mobile navigation menu has opened, if a login form’s contents are being authenticated by the server, and so forth.&lt;/p&gt;
&lt;p&gt;Some interactions naturally take longer than others, but for especially complex interactions, it’s important to quickly present some initial visual feedback to tell the user that something is happening. The next frame that the browser will paint is the earliest opportunity to do this.&lt;/p&gt;
&lt;p&gt;Therefore, the intent of INP is not to measure all the eventual effects of an interaction—such as network fetches and UI updates from other asynchronous operations—but the time that the next paint is being blocked. By delaying visual feedback, users may get the impression that the page is not responding quickly enough, and INP was developed to help developers measure this part of the user experience.&lt;/p&gt;
&lt;p&gt;In the following video, the example on the right gives immediate visual feedback that an accordion is opening. Poor responsiveness is demonstrated in the example on the left, and how it can create poor user experiences.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/883d739c4b2d516454b0b83c7b84ec4a/inp-responsiveness.gif&quot; alt=&quot;INP&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An example of poor versus good responsiveness. On the left, long tasks block the accordion from opening. This causes the user to click multiple times, thinking the experience is broken. When the main thread catches up, it processes the delayed inputs, resulting in the accordion opening and closing unexpectedly. On the right, a more responsive page opens the accordion quickly and without incident.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;first-input-delay-fid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#first-input-delay-fid&quot; aria-label=&quot;first input delay fid permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;First Input Delay (FID)&lt;/h2&gt;
&lt;p&gt;As of March 2024, Interaction to Next Paint (INP) will replace the First Input Delay (FID) as a new Core Web Vital.&lt;/p&gt;
&lt;p&gt;First Input Delay is a web performance metric that measures the time between a user’s very first interaction with a web page and the time when the browser’s main thread is able to start processing that interaction event.&lt;/p&gt;
&lt;p&gt;When a user interacts with a web page, an event is added to a queue to be processed by the browser’s main thread. However, if the main thread is already busy doing other tasks like parsing HTML, executing JavaScript, or handling other event listeners, the new event has to wait in the queue.&lt;/p&gt;
&lt;p&gt;The FID metric captures the duration of this waiting time, which tells us how long it takes for the browser to respond to the user’s first input while the main thread is busy.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2c84e1d64f28baec581a7b09f9374a41/b0376/fid.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAciCQL//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAZEAEAAgMAAAAAAAAAAAAAAAABABEQIUH/2gAIAQEAAT8hsTZGjmP/2gAMAwEAAgADAAAAEHQP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAAIDAQAAAAAAAAAAAAAAAQARIVFhsf/aAAgBAQABPxA1L2Ur9xRcFT//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;FID&quot;
        title=&quot;FID&quot;
        src=&quot;/static/2c84e1d64f28baec581a7b09f9374a41/6a068/fid.jpg&quot;
        srcset=&quot;/static/2c84e1d64f28baec581a7b09f9374a41/09b79/fid.jpg 240w,
/static/2c84e1d64f28baec581a7b09f9374a41/7cc5e/fid.jpg 480w,
/static/2c84e1d64f28baec581a7b09f9374a41/6a068/fid.jpg 960w,
/static/2c84e1d64f28baec581a7b09f9374a41/644c5/fid.jpg 1440w,
/static/2c84e1d64f28baec581a7b09f9374a41/b0376/fid.jpg 1784w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, the First Input Delay (FID) metric has some shortcomings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FID only considers the delay of the first input event, ignoring subsequent interactions that may also be slow or even slower.&lt;/li&gt;
&lt;li&gt;Other factors can contribute to a longer visual feedback delay between user interactions, which FID doesn’t measure. This includes the time it takes to process event handlers and recalculate the layout before providing visual feedback to the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;interaction-to-next-paint-inp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#interaction-to-next-paint-inp&quot; aria-label=&quot;interaction to next paint inp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Interaction to Next Paint (INP)&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a4d5b9d15627637aca5c705ebb148697/d6f75/inp-measurement.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe5dAg//xAAXEAADAQAAAAAAAAAAAAAAAAABESAh/9oACAEBAAEFAlon/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAASAx/9oACAEBAAY/AqW//8QAGxAAAgEFAAAAAAAAAAAAAAAAARAhABFBceH/2gAIAQEAAT8hPYqAg32sv//aAAwDAQACAAMAAAAQQ8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAwEBAQAAAAAAAAAAAAABABEhMUFR/9oACAEBAAE/EAWdbceQLia9XDkQ1WwAuvty2f/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;INP&quot;
        title=&quot;INP&quot;
        src=&quot;/static/a4d5b9d15627637aca5c705ebb148697/6a068/inp-measurement.jpg&quot;
        srcset=&quot;/static/a4d5b9d15627637aca5c705ebb148697/09b79/inp-measurement.jpg 240w,
/static/a4d5b9d15627637aca5c705ebb148697/7cc5e/inp-measurement.jpg 480w,
/static/a4d5b9d15627637aca5c705ebb148697/6a068/inp-measurement.jpg 960w,
/static/a4d5b9d15627637aca5c705ebb148697/644c5/inp-measurement.jpg 1440w,
/static/a4d5b9d15627637aca5c705ebb148697/0f98f/inp-measurement.jpg 1920w,
/static/a4d5b9d15627637aca5c705ebb148697/d6f75/inp-measurement.jpg 8000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To address these limitations, the Interaction to Next Paint (INP) metric will replace First Input Delay. INP is a metric that assesses a page’s overall responsiveness to user interactions by observing the latency of all &lt;code class=&quot;language-text&quot;&gt;click, tap, and keyboard&lt;/code&gt; interactions that occur &lt;strong&gt;throughout the lifespan&lt;/strong&gt; of a user’s visit to a page. The final INP value is the longest interaction observed, ignoring outliers.&lt;/p&gt;
&lt;p&gt;While FID only measured the input delay, which is the time between user input and the browser starting to execute the event handler, INP measures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input Delay: the time between user interaction and the time the browser is able to process the event, similar to FID.&lt;/li&gt;
&lt;li&gt;Processing Delay: the time it takes the browser to process the event handlers.&lt;/li&gt;
&lt;li&gt;Presentational Delay: the time it takes the browser to recalculate the layout and paint the pixels to the screen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/68244b23822f59634006d5f45cd33c51/b0376/inp.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABjhAP/8QAFRABAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQEAAQUCr//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABkQAAIDAQAAAAAAAAAAAAAAAAABESExQf/aAAgBAQABPyGywUPgz//aAAwDAQACAAMAAAAQ/D//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEAAgMAAAAAAAAAAAAAAAABACExUbH/2gAIAQEAAT8QAg4G4qu4Qaon/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;INP&quot;
        title=&quot;INP&quot;
        src=&quot;/static/68244b23822f59634006d5f45cd33c51/6a068/inp.jpg&quot;
        srcset=&quot;/static/68244b23822f59634006d5f45cd33c51/09b79/inp.jpg 240w,
/static/68244b23822f59634006d5f45cd33c51/7cc5e/inp.jpg 480w,
/static/68244b23822f59634006d5f45cd33c51/6a068/inp.jpg 960w,
/static/68244b23822f59634006d5f45cd33c51/644c5/inp.jpg 1440w,
/static/68244b23822f59634006d5f45cd33c51/b0376/inp.jpg 1784w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Additionally, whereas FID only measured the very first user interaction, the INP score is measured when the user leaves the page by aggregating all interactions the user made with the page throughout the page’s lifetime and returning the worst-measured score.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/154a4d4a7d67ed7ac29a6295656528c3/03a73/inp-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHkMVCD/8QAFhABAQEAAAAAAAAAAAAAAAAAEQAg/9oACAEBAAEFAiM//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAABEBEEFR/9oACAEBAAE/IWWDcJHf/9oADAMBAAIAAwAAABA33//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAQACAgMBAAAAAAAAAAAAAAEAEUGREFFhgf/aAAgBAQABPxAQJb7PBuBGkqU6mM74/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;INP&quot;
        title=&quot;INP&quot;
        src=&quot;/static/154a4d4a7d67ed7ac29a6295656528c3/6a068/inp-02.jpg&quot;
        srcset=&quot;/static/154a4d4a7d67ed7ac29a6295656528c3/09b79/inp-02.jpg 240w,
/static/154a4d4a7d67ed7ac29a6295656528c3/7cc5e/inp-02.jpg 480w,
/static/154a4d4a7d67ed7ac29a6295656528c3/6a068/inp-02.jpg 960w,
/static/154a4d4a7d67ed7ac29a6295656528c3/644c5/inp-02.jpg 1440w,
/static/154a4d4a7d67ed7ac29a6295656528c3/03a73/inp-02.jpg 1782w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;With INP, we no longer have to focus solely on optimizing event queuing times and main thread availability, as was the case with FID. Now, it is also crucial to address the entire lifecycle of a user interaction. This includes processing event handlers, recalculating layouts, and painting updates to the screen, all of which are critical components of the INP metric.&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-good-inp-score&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-a-good-inp-score&quot; aria-label=&quot;what is a good inp score permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is a good INP score?&lt;/h3&gt;
&lt;p&gt;Pinning labels such as “good” or “poor” on a responsiveness metric is difficult. On one hand, you want to encourage development practices that prioritize good responsiveness. On the other hand, you must account for the fact that there’s considerable variability in the capabilities of devices people use to set achievable development expectations.&lt;/p&gt;
&lt;p&gt;To ensure you’re delivering user experiences with good responsiveness, a good threshold to measure is the 75th percentile of page loads recorded in the field, segmented across mobile and desktop devices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An INP below or at &lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; milliseconds means a page has good responsiveness.&lt;/li&gt;
&lt;li&gt;An INP above &lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; milliseconds and below or at &lt;code class=&quot;language-text&quot;&gt;500&lt;/code&gt; milliseconds means a page’s responsiveness needs improvement.&lt;/li&gt;
&lt;li&gt;An INP above &lt;code class=&quot;language-text&quot;&gt;500&lt;/code&gt; milliseconds means a page has poor responsiveness.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f6c800d0012517e6b60faebc74e1ff41/53ff1/inp-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.333333333333332%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABuACP/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAERAiH/2gAIAQEAAQUCnXkSh//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAQEBAAAAAAAAAAAAAAAAAAABMf/aAAgBAQAGPwJtbX//xAAaEAACAgMAAAAAAAAAAAAAAAAAEQFxMUGB/9oACAEBAAE/IdrmmMlr6JZrk//aAAwDAQACAAMAAAAQg/8A/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EG1//8QAGBEAAgMAAAAAAAAAAAAAAAAAAAERITH/2gAIAQIBAT8Qd6Qf/8QAGhAAAwEAAwAAAAAAAAAAAAAAAREhAGGBkf/aAAgBAQABPxDt/B5iFByoQNMrLbN//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;INP&quot;
        title=&quot;INP&quot;
        src=&quot;/static/f6c800d0012517e6b60faebc74e1ff41/6a068/inp-score.jpg&quot;
        srcset=&quot;/static/f6c800d0012517e6b60faebc74e1ff41/09b79/inp-score.jpg 240w,
/static/f6c800d0012517e6b60faebc74e1ff41/7cc5e/inp-score.jpg 480w,
/static/f6c800d0012517e6b60faebc74e1ff41/6a068/inp-score.jpg 960w,
/static/f6c800d0012517e6b60faebc74e1ff41/644c5/inp-score.jpg 1440w,
/static/f6c800d0012517e6b60faebc74e1ff41/53ff1/inp-score.jpg 1824w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-is-an-interaction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-an-interaction&quot; aria-label=&quot;what is an interaction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is an interaction?&lt;/h3&gt;
&lt;p&gt;For the purposes of INP, only the following interaction types are observed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Clicking&lt;/code&gt; with a mouse.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Tapping&lt;/code&gt; on a device with a touchscreen.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Pressing&lt;/code&gt; a key on either a physical or onscreen keyboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cd496eee8f26c76b367b170373c0cfda/d6f75/inp-interaction.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHt3aCK/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFxAAAwEAAAAAAAAAAAAAAAAAACAx4f/aAAgBAQAGPwLSL//EABkQAQADAQEAAAAAAAAAAAAAAAEAECExEf/aAAgBAQABPyHrgwKHXkNKa//aAAwDAQACAAMAAAAQfA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAEAAgICAwAAAAAAAAAAAAABABEhMRBRcZHR/9oACAEBAAE/EEs2c7fYK8i209sSCVT08CymawT/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;INP&quot;
        title=&quot;INP&quot;
        src=&quot;/static/cd496eee8f26c76b367b170373c0cfda/6a068/inp-interaction.jpg&quot;
        srcset=&quot;/static/cd496eee8f26c76b367b170373c0cfda/09b79/inp-interaction.jpg 240w,
/static/cd496eee8f26c76b367b170373c0cfda/7cc5e/inp-interaction.jpg 480w,
/static/cd496eee8f26c76b367b170373c0cfda/6a068/inp-interaction.jpg 960w,
/static/cd496eee8f26c76b367b170373c0cfda/644c5/inp-interaction.jpg 1440w,
/static/cd496eee8f26c76b367b170373c0cfda/0f98f/inp-interaction.jpg 1920w,
/static/cd496eee8f26c76b367b170373c0cfda/d6f75/inp-interaction.jpg 8000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Interactions can consist of multiple events. For example, a keystroke includes the &lt;code class=&quot;language-text&quot;&gt;keydown&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;keypress&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;keyup&lt;/code&gt; events. Tap interactions contain &lt;code class=&quot;language-text&quot;&gt;pointerup&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;pointerdown&lt;/code&gt; events. The event with the longest duration within the interaction is what contributes to the interaction’s total latency.&lt;/p&gt;
&lt;h2 id=&quot;the-most-important-avoiding-long-tasks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-most-important-avoiding-long-tasks&quot; aria-label=&quot;the most important avoiding long tasks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The most important: Avoiding long tasks&lt;/h2&gt;
&lt;p&gt;Common advice for keeping JavaScript apps fast tends to boil down to the following advice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Don’t block the main thread.”&lt;/li&gt;
&lt;li&gt;“Break up your long tasks.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3794294b550344d39bff0cc6f5c3329c/d6f75/avoid-long-tasks-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHaW4IGD//EABkQAQACAwAAAAAAAAAAAAAAAAEAERASMf/aAAgBAQABBQJZsw5RKMf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAbEAACAQUAAAAAAAAAAAAAAAAAMQECEBEhMv/aAAgBAQAGPwLVUHUDyIVv/8QAHRAAAgIBBQAAAAAAAAAAAAAAAAERMUEhYYGRof/aAAgBAQABPyHXfAYsgvPoJMDYdFUf/9oADAMBAAIAAwAAABADz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQADAQEBAAAAAAAAAAAAAAEAESFRMWH/2gAIAQEAAT8QpASOh52WTHe1DuBqqFRZaj1IF5r8QAUAOE//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;INP&quot;
        title=&quot;INP&quot;
        src=&quot;/static/3794294b550344d39bff0cc6f5c3329c/6a068/avoid-long-tasks-01.jpg&quot;
        srcset=&quot;/static/3794294b550344d39bff0cc6f5c3329c/09b79/avoid-long-tasks-01.jpg 240w,
/static/3794294b550344d39bff0cc6f5c3329c/7cc5e/avoid-long-tasks-01.jpg 480w,
/static/3794294b550344d39bff0cc6f5c3329c/6a068/avoid-long-tasks-01.jpg 960w,
/static/3794294b550344d39bff0cc6f5c3329c/644c5/avoid-long-tasks-01.jpg 1440w,
/static/3794294b550344d39bff0cc6f5c3329c/0f98f/avoid-long-tasks-01.jpg 1920w,
/static/3794294b550344d39bff0cc6f5c3329c/d6f75/avoid-long-tasks-01.jpg 8000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This is great advice, but what work does it involve? Shipping less JavaScript is good, but does that automatically equate to more responsive user interfaces? Maybe, but maybe not.&lt;/p&gt;
&lt;p&gt;To understand how to optimize tasks in JavaScript, you first need to know what tasks are, and how the browser handles them.&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-task&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-a-task&quot; aria-label=&quot;what is a task permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is a task?&lt;/h3&gt;
&lt;p&gt;A task is any discrete piece of work that the browser does. That work includes rendering, parsing HTML and CSS, running JavaScript, and other types of work you may not have direct control over. Of all of this, the JavaScript you write is perhaps the largest source of tasks.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/18e947729b2f0f79ac227be7917cfdcf/d7542/avoid-long-tasks-02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABWklEQVQY03WNS0vDUBCF8x9dCJW6dyuIiCsRKRb/ghuXiiDqwmfBR7VRsFS0Jrk1uc1t7YMa6TVU0zsPiVIR1OEsDmfmO2NJKYvF02q16jieK/ya3xCPSjyGrqg/uNLx6q5I5XjSEcoV0g9cz3M8z202mlYUPd9c3YWy2W1Lv7Jl78zfFZYqh7ngclld5ZWdl5fLspSXpZWguFAvr+1vhrfX9aDWeo56FiAM35mAmOGtc6HsXMNevNkYq+1NqKOMOs6+2lldyvaL43FlNgnX/XuOOol559dYW8YYJCRCYqah5qQbh9vh0WRQmJIn0/Igo8sz/fJcLFbfogoMWsyMBMystbYAgIg4HWJCZAYtzNMudA5M73yo1pPuWdIrQfJiYJAeISPgb/hzhTDyjEhfGTMRgkl0+iDN/4YZiTgVpj1omODLE8Jw0B6VjmBjDP0YRPzHg4nVZzN9wx/6dLJOVW0A5QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;INP&quot;
        title=&quot;INP&quot;
        src=&quot;/static/18e947729b2f0f79ac227be7917cfdcf/d7542/avoid-long-tasks-02.png&quot;
        srcset=&quot;/static/18e947729b2f0f79ac227be7917cfdcf/8ff5a/avoid-long-tasks-02.png 240w,
/static/18e947729b2f0f79ac227be7917cfdcf/e85cb/avoid-long-tasks-02.png 480w,
/static/18e947729b2f0f79ac227be7917cfdcf/d7542/avoid-long-tasks-02.png 810w&quot;
        sizes=&quot;(max-width: 810px) 100vw, 810px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
A task started by a click event handler in, shown in Chrome DevTools’ performance profiler.&lt;/p&gt;
&lt;p&gt;Tasks associated with JavaScript impact performance in a couple of ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a browser downloads a JavaScript file during startup, it queues tasks to parse and compile that JavaScript so it can be executed later.&lt;/li&gt;
&lt;li&gt;At other times during the life of the page, tasks are queued when JavaScript does work such as responding to interactions through event handlers, JavaScript-driven animations, and background activity such as analytics collection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this stuff—with the exception of web workers and similar APIs—happens on the &lt;code class=&quot;language-text&quot;&gt;main thread&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-main-thread&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-the-main-thread&quot; aria-label=&quot;what is the main thread permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is the main thread?&lt;/h3&gt;
&lt;p&gt;The main thread is where most tasks run in the browser, and where almost all JavaScript you write is executed.&lt;/p&gt;
&lt;p&gt;The main thread can only process &lt;code class=&quot;language-text&quot;&gt;one task at a time&lt;/code&gt;. Any task that takes longer than &lt;code class=&quot;language-text&quot;&gt;50&lt;/code&gt; milliseconds is a long task. For tasks that exceed &lt;code class=&quot;language-text&quot;&gt;50&lt;/code&gt; milliseconds, the task’s total time minus 50 milliseconds is known as the task’s blocking period.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d5ee3b19180f37e369924fe643600542/21cdd/avoid-long-tasks-03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.750000000000002%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAApUlEQVQI1z3Iuw6CMABAUT7HGeqg/LKbE9pEC7Gtj2AdFIEohPLU0AJ1MHExBGNyl3O1m394FtcyPZe5V1+odNeS2UOCIcGQ7BsO+p2jLU/ogfeaZ01KOlXMbI9mtxsrClqid0Rvia6o8doCRUFP/KfRYtCRUeXMNerOggSGHIbJMiqRz2EmcSpwJnFc2V5kBRymYpNJEleOFy/uxSpvCK+d96f5Av6YjA7yo/HeAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A long task as depicted in Chrome&amp;#39;s performance profiler. Long tasks are indicated by a red triangle in the corner of the task, with the blocking portion of the task filled in with a pattern of diagonal red stripes.&quot;
        title=&quot;A long task as depicted in Chrome&amp;#39;s performance profiler. Long tasks are indicated by a red triangle in the corner of the task, with the blocking portion of the task filled in with a pattern of diagonal red stripes.&quot;
        src=&quot;/static/d5ee3b19180f37e369924fe643600542/d9199/avoid-long-tasks-03.png&quot;
        srcset=&quot;/static/d5ee3b19180f37e369924fe643600542/8ff5a/avoid-long-tasks-03.png 240w,
/static/d5ee3b19180f37e369924fe643600542/e85cb/avoid-long-tasks-03.png 480w,
/static/d5ee3b19180f37e369924fe643600542/d9199/avoid-long-tasks-03.png 960w,
/static/d5ee3b19180f37e369924fe643600542/07a9c/avoid-long-tasks-03.png 1440w,
/static/d5ee3b19180f37e369924fe643600542/21cdd/avoid-long-tasks-03.png 1680w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
A long task as depicted in Chrome’s performance profiler. Long tasks are indicated by a red triangle in the corner of the task, with the blocking portion of the task filled in with a pattern of diagonal red stripes.&lt;/p&gt;
&lt;p&gt;To prevent the main thread from being blocked for too long, you can break up a long task into several smaller ones.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c0b12cd648941371f5ff54a80b1c15ff/b7936/avoid-long-tasks-04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0UlEQVQY03WQbQvBUBiGz///CcQXSilK2jDyDS3JhLxOHMbZzHaWlx2zcx4hCu3u6vl0fbjvB8FfhBAAMBhOW6qm9cYzfQ0RQTfmcrf5g6Ct40YxxgVLlyku/wkN4akedZB3wOFe5j9YEtDqxSwzUgJX4Zb0LUjcrmzwEkVVCgJOj6EfABcQWfu5kr+veE5+6AfbbqvNTltljL0+8RHeAHLM+dXIrroxZuQvpOYt0tt+nO1kZirWKOnMUj5RrqSItdgZZ31SP60y614iJDl9PrkDMZlLeNKcIacAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A visualization of a single long task versus that same task broken up into five shorter tasks.&quot;
        title=&quot;A visualization of a single long task versus that same task broken up into five shorter tasks.&quot;
        src=&quot;/static/c0b12cd648941371f5ff54a80b1c15ff/d9199/avoid-long-tasks-04.png&quot;
        srcset=&quot;/static/c0b12cd648941371f5ff54a80b1c15ff/8ff5a/avoid-long-tasks-04.png 240w,
/static/c0b12cd648941371f5ff54a80b1c15ff/e85cb/avoid-long-tasks-04.png 480w,
/static/c0b12cd648941371f5ff54a80b1c15ff/d9199/avoid-long-tasks-04.png 960w,
/static/c0b12cd648941371f5ff54a80b1c15ff/b7936/avoid-long-tasks-04.png 1155w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
A visualization of a single long task versus that same task broken up into five shorter tasks.&lt;/p&gt;
&lt;h3 id=&quot;task-management-strategies&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#task-management-strategies&quot; aria-label=&quot;task management strategies permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Task management strategies&lt;/h3&gt;
&lt;p&gt;A common piece of advice in software architecture is to break up your work into smaller functions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSettings&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;validateForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;showSpinner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;saveToDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updateUI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sendAnalytics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this example, there’s a function named saveSettings() that calls five functions to validate a form, show a spinner, send data to the application backend, update the user interface, and send analytics.&lt;/p&gt;
&lt;p&gt;Conceptually, saveSettings() is well-architected. If you need to debug one of these functions, you can traverse the project tree to figure out what each function does. Breaking up work like this makes projects easier to navigate and maintain.&lt;/p&gt;
&lt;p&gt;A potential problem here, though, is that JavaScript doesn’t run each of these functions as separate tasks because they are executed within the saveSettings() function. &lt;strong&gt;This means that all five functions will run as one task.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f29580057d3a7258b5eacdc43a99172f/21cdd/avoid-long-tasks-06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.083333333333332%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAqklEQVQI1y3C3QqCMBgAUB+3ex+gt+kNoouwvBJEwRDNTbf817bQfdOi/IEF0eFoGTqX+NgSo6EnFu57a9fVNy6A88fYiw9INS1qXtQ0/8+rWt+FYfK40qizjcxNH+oQ6eK3zQ9lealrN83slvlySARgARhkLCQWkoje6aj1HJSWlNinXsppVAQpJ6gIG0jYmPCR5Cy4EptUHhviFjDKXJS5d8ANoNcyKKW+IjqdkUE2ywYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A single function saveSettings() that calls five functions. The work is run as part of one long monolithic task, blocking any visual response until all five functions are complete.&quot;
        title=&quot;A single function saveSettings() that calls five functions. The work is run as part of one long monolithic task, blocking any visual response until all five functions are complete.&quot;
        src=&quot;/static/f29580057d3a7258b5eacdc43a99172f/d9199/avoid-long-tasks-06.png&quot;
        srcset=&quot;/static/f29580057d3a7258b5eacdc43a99172f/8ff5a/avoid-long-tasks-06.png 240w,
/static/f29580057d3a7258b5eacdc43a99172f/e85cb/avoid-long-tasks-06.png 480w,
/static/f29580057d3a7258b5eacdc43a99172f/d9199/avoid-long-tasks-06.png 960w,
/static/f29580057d3a7258b5eacdc43a99172f/07a9c/avoid-long-tasks-06.png 1440w,
/static/f29580057d3a7258b5eacdc43a99172f/21cdd/avoid-long-tasks-06.png 1680w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the best case scenario, even just one of those functions can contribute 50 milliseconds or more to the task’s total length. In the worst case, more of those tasks can run much longer—especially on resource-constrained devices.&lt;/p&gt;
&lt;p&gt;In this case, saveSettings() is triggered by a user click, and because the browser isn’t able to show a response until the entire function is finished running, the result of this long task is a slow and unresponsive UI, and will be measured as a poor Interaction to Next Paint (INP).&lt;/p&gt;
&lt;h3 id=&quot;manually-defer-code-execution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#manually-defer-code-execution&quot; aria-label=&quot;manually defer code execution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Manually defer code execution&lt;/h3&gt;
&lt;p&gt;To make sure important user-facing tasks and UI responses happen before lower-priority tasks, you can yield to the main thread by briefly interrupting your work to give the browser opportunities to run more important tasks.&lt;/p&gt;
&lt;p&gt;One method developers have used to break up tasks into smaller ones involves setTimeout(). With this technique, you pass the function to setTimeout(). This postpones execution of the callback into a separate task, even if you specify a timeout of &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;function saveSettings () {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; // Do critical work that is user-visible:
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; validateForm();
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; showSpinner();
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; updateUI();
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; // Defer work that isn&apos;t user-visible to a separate task:
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  setTimeout(() =&gt; {
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   saveToDatabase();
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   sendAnalytics();
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  }, 0);
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is known as &lt;strong&gt;yielding&lt;/strong&gt;, and it works best for a series of functions that need to run sequentially.&lt;/p&gt;
&lt;p&gt;However, your code may not always be organized this way. For example, you could have a large amount of data that needs to be processed in a loop, and that task could take a very long time if there are many iterations.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;processData&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; largeDataArray&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Process the individual item here.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using setTimeout() here is problematic because of developer ergonomics, and after five rounds of nested setTimeout()s, the browser will start imposing a minimum &lt;code class=&quot;language-text&quot;&gt;4 millisecond&lt;/code&gt; delay for each additional setTimeout().&lt;/p&gt;
&lt;h3 id=&quot;a-dedicated-yielding-api-scheduleryield&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-dedicated-yielding-api-scheduleryield&quot; aria-label=&quot;a dedicated yielding api scheduleryield permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A dedicated yielding API: scheduler.yield()&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;scheduler.yield()&lt;/code&gt; is an API specifically designed for yielding to the main thread in the browser. Currently supported on &lt;code class=&quot;language-text&quot;&gt;Chrome 129&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s not language-level syntax or a special construct; &lt;code class=&quot;language-text&quot;&gt;scheduler.yield()&lt;/code&gt; is just a function that returns a Promise that will be resolved in a future task. Any code chained to run after that Promise is resolved (either in an explicit &lt;code class=&quot;language-text&quot;&gt;.then()&lt;/code&gt; chain or after awaiting it in an async function) will then run in that future task.&lt;/p&gt;
&lt;p&gt;In practice: insert an await &lt;code class=&quot;language-text&quot;&gt;scheduler.yield()&lt;/code&gt; and the function will pause execution at that point and yield to the main thread. The execution of the rest of the function—called the continuation of the function—will be scheduled to run in a new event-loop task. When that task starts, the awaited promise will be resolved, and the function will continue executing where it left off.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSettings&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Do critical work that is user-visible:&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;validateForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;showSpinner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updateUI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Yield to the main thread:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Work that isn&apos;t user-visible, continued in a separate task:&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;saveToDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sendAnalytics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0894829a5a6403d9f9c967c94179d873/21cdd/avoid-long-tasks-07.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.083333333333332%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvklEQVQI1wXBaw7BMAAA4F2Pn+7gFm5BxCO4giD455GYEK8xNWZbZh2tsrVLTLZZ6/skWxu4ly42etjouJeheWhDrUOwQV7eE6Ek4RF9+HLNl6t0XqfzuifXmFy6jfvH/V3SlnllkkEgR0AW6oXRqjxTi9ZzZ2NoIVPVwdnZnNwWgA3FrChGBThNFZad28JHXFrZYKqvt/g6MbcWQ/Qbs+TH4jCIAz+khJH3h7KUB6l4h/HrEwU/zlIRcSGE+APBmpiPc3mmkgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The execution of the function saveSettings() is now split over two tasks. As a result, layout and paint can run between the tasks, giving the user a quicker visual response, as measured by the now much shorter pointer interaction.&quot;
        title=&quot;The execution of the function saveSettings() is now split over two tasks. As a result, layout and paint can run between the tasks, giving the user a quicker visual response, as measured by the now much shorter pointer interaction.&quot;
        src=&quot;/static/0894829a5a6403d9f9c967c94179d873/d9199/avoid-long-tasks-07.png&quot;
        srcset=&quot;/static/0894829a5a6403d9f9c967c94179d873/8ff5a/avoid-long-tasks-07.png 240w,
/static/0894829a5a6403d9f9c967c94179d873/e85cb/avoid-long-tasks-07.png 480w,
/static/0894829a5a6403d9f9c967c94179d873/d9199/avoid-long-tasks-07.png 960w,
/static/0894829a5a6403d9f9c967c94179d873/07a9c/avoid-long-tasks-07.png 1440w,
/static/0894829a5a6403d9f9c967c94179d873/21cdd/avoid-long-tasks-07.png 1680w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The real benefit of scheduler.yield() over other yielding approaches, though, is that its continuation is prioritized, which means that if you yield in the middle of a task, the continuation of the current task will run before any other similar tasks are started.&lt;/p&gt;
&lt;p&gt;This avoids code from other task sources from interrupting the order of your code’s execution, such as tasks from third-party scripts.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bd600553d491b6cca133c735daa9fb61/58354/avoid-long-tasks-08.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4UlEQVQY02XF7U8SAQDH8fu3epMv3FpveqL1MJdbC8tyiqsT8B4iBzQHRzXLnlZBljUNtU3jOHZJCz1YIHEcN+YSpwEH9wScHV14YNdGb9r67LvfD1DVH1WupDRqVa7EV7hOWzf+cfD3fnf7D4DH2N6BR8ct/lOjL/rtM9fcCxfQOdONlydsc3A/Qh01IQNnevwjPffMFmjMgTogCEZRBwiORaNRoCIqn9PbcXo3zpa/bEnpTT5JlxOZUizLMXFG+bTGJNeJnRT5LZ6mN1gmyzJ0jqGzmbQg8EBVkFOZfCq7SVMJNZcT+e8FpbhTL0pCRRCFkiyJklyviAK/V5E1TmqWpSYnN4uCqmo6EF7Pn7fNXpwMTfRZE73HRobOHQoMH8YuWexWyA4jMAJByDhkOz3kNYGzg87gFWfwqmvhyODT9x9pQNM0SZJrXUq90ag3pJpcq8k/9+ROq6m3VL2ltltqp72v6YamG7+6q+0bnQMDoPO706/Jx8G1t1P+r5O+mcC0O/zcvfhs6g35jswtrbJLq2yQZF+tJENhIkLgOB6KRIgPK8uFQgGIbWyPepatT2IPr3tyfWbnreGTi+jZwE2zex68g4/fD8MPCPAufnli3nUb82GYx+vFMJ/L5aIo6g/5iGHAI/x+8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;When you use scheduler.yield(), the continuation picks up where it left off before moving on to other tasks.&quot;
        title=&quot;When you use scheduler.yield(), the continuation picks up where it left off before moving on to other tasks.&quot;
        src=&quot;/static/bd600553d491b6cca133c735daa9fb61/d9199/avoid-long-tasks-08.png&quot;
        srcset=&quot;/static/bd600553d491b6cca133c735daa9fb61/8ff5a/avoid-long-tasks-08.png 240w,
/static/bd600553d491b6cca133c735daa9fb61/e85cb/avoid-long-tasks-08.png 480w,
/static/bd600553d491b6cca133c735daa9fb61/d9199/avoid-long-tasks-08.png 960w,
/static/bd600553d491b6cca133c735daa9fb61/58354/avoid-long-tasks-08.png 1396w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;cross-browser-support&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cross-browser-support&quot; aria-label=&quot;cross browser support permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cross-browser support&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;scheduler.yield()&lt;/code&gt; is not yet supported in all browsers, so a fallback is needed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yieldToMain&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;globalThis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scheduler&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;yield&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Fall back to yielding with setTimeout.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Managing tasks is challenging, but doing so ensures that your page responds more quickly to user interactions. There’s no one single piece of advice for managing and prioritizing tasks, but rather a number of different techniques. To reiterate, these are the main things you’ll want to consider when managing tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yield to the main thread for critical, user-facing tasks.&lt;/li&gt;
&lt;li&gt;Use scheduler.yield() (with a cross-browser fallback) to ergonomically yield and get prioritized continuations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vercel.com/blog/first-input-delay-vs-interaction-to-next-paint&quot;&gt;Vercel: First Input Delay (FID) vs. Interaction to Next Paint (INP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/inp&quot;&gt;Web.dev: Interaction to Next Paint (INP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/optimize-long-tasks&quot;&gt;Web.dev: Optimize Long Tasks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[An error occurred while installing pg (1.5.6), and Bundler cannot continue (when running rails new)]]></title><link>https://trungvose.coman-error-occurred-while-installing-pg-and-bundler-cannot-continue/</link><guid isPermaLink="false">https://trungvose.coman-error-occurred-while-installing-pg-and-bundler-cannot-continue/</guid><pubDate>Sat, 20 Jul 2024 06:50:00 GMT</pubDate><content:encoded>&lt;p&gt;I am installing &lt;code class=&quot;language-text&quot;&gt;rails&lt;/code&gt; to demonstrate how a traditional server-side rendered web application works on my Mac M2, Sonoma 14.5.&lt;/p&gt;
&lt;p&gt;I have followed the instructions from &lt;a href=&quot;https://gorails.com/setup/macos/14-sonoma&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;install-ruby&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#install-ruby&quot; aria-label=&quot;install ruby permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Install Ruby&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/excid3/asdf.git ~/.asdf
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;. &quot;$HOME/.asdf/asdf.sh&quot;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; ~/.zshrc
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;. &quot;$HOME/.asdf/completions/asdf.bash&quot;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; ~/.zshrc
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;legacy_version_file = yes&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; ~/.asdfrc
&lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token environment constant&quot;&gt;$SHELL&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;asdf plugin &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; ruby
asdf &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ruby &lt;span class=&quot;token number&quot;&gt;3.3&lt;/span&gt;.3
asdf global ruby &lt;span class=&quot;token number&quot;&gt;3.3&lt;/span&gt;.3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Confirm that the default Ruby version matches the installed version:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;which&lt;/span&gt; ruby
&lt;span class=&quot;token comment&quot;&gt;#=&gt; /Users/username/.asdf/shims/ruby&lt;/span&gt;
ruby &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#=&gt; 3.3.3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;installing-rails&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#installing-rails&quot; aria-label=&quot;installing rails permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Installing Rails&lt;/h3&gt;
&lt;p&gt;To install Rails, run the following command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gem &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; rails &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7.1&lt;/span&gt;.3.4
rails &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Rails 7.1.3.4&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;create-a-new-rails-app&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#create-a-new-rails-app&quot; aria-label=&quot;create a new rails app permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Create a new Rails app&lt;/h3&gt;
&lt;p&gt;To create a new Rails app, use the following command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rails new music-rails &lt;span class=&quot;token parameter variable&quot;&gt;--database&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;postgresql&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here, I encountered the following error as mentioned on the blog title:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;Fetching gem metadata from https://rubygems.org/.........
Installing pg 1.5.6 with native extensions
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   current directory: /Users/trung.vo/.asdf/installs/ruby/3.3.3/lib/ruby/gems/3.3.0/gems/pg-1.5.6/ext
&lt;/span&gt;/Users/trung.vo/.asdf/installs/ruby/3.3.3/bin/ruby extconf.rb
Calling libpq with GVL unlocked
checking for pg_config... no
checking for libpq per pkg-config... no
Using libpq from 
checking for libpq-fe.h... no
Can&apos;t find the &apos;libpq-fe.h header
&lt;span class=&quot;token coord&quot;&gt;*****************************************************************************&lt;/span&gt;

&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; Unable to find PostgreSQL client library.
&lt;/span&gt;
Please install libpq or postgresql client package like so:
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; brew install libpq
&lt;/span&gt;
or try again with:
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; gem install pg -- --with-pg-config=/path/to/pg_config
&lt;/span&gt;
or set library paths manually with:
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; gem install pg -- --with-pg-include=/path/to/libpq-fe.h/ --with-pg-lib=/path/to/libpq.so/
&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;*** extconf.rb failed ***&lt;/span&gt;
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

&lt;span class=&quot;token coord&quot;&gt;*** removed for brevity ***&lt;/span&gt;

&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; An error occurred while installing pg (1.5.6), and Bundler cannot continue.
&lt;/span&gt;
In Gemfile:
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; pg
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    run  bundle lock --add-platform=x86_64-linux
&lt;/span&gt;Writing lockfile to /Users/trung.vo/Source/view-transitions-demo/music-rails/Gemfile.lock
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    run  bundle lock --add-platform=aarch64-linux
&lt;/span&gt;Writing lockfile to /Users/trung.vo/Source/view-transitions-demo/music-rails/Gemfile.lock
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    run  bundle binstubs bundler
&lt;/span&gt;Could not find pg-1.5.6 in locally installed gems
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    rails  importmap:install
&lt;/span&gt;Could not find pg-1.5.6 in locally installed gems
Run `bundle install` to install missing gems.
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    rails  turbo:install stimulus:install
&lt;/span&gt;Could not find pg-1.5.6 in locally installed gems
Run `bundle install` to install missing gems.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ed2625c1b1c22922dae239bda5490ba2/34601/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABygyGo//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAIDAAAAAAAAAAAAAAAAAAEQADFB/9oACAEBAAE/ITTyFf/aAAwDAQACAAMAAAAQo8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEQITFB4f/aAAgBAQABPxBlTmNX7hDpAU6x/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;an error occurred while installing pg (1.5.6), and Bundler cannot continue.&quot;
        title=&quot;an error occurred while installing pg (1.5.6), and Bundler cannot continue.&quot;
        src=&quot;/static/ed2625c1b1c22922dae239bda5490ba2/6a068/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg&quot;
        srcset=&quot;/static/ed2625c1b1c22922dae239bda5490ba2/09b79/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 240w,
/static/ed2625c1b1c22922dae239bda5490ba2/7cc5e/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 480w,
/static/ed2625c1b1c22922dae239bda5490ba2/6a068/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 960w,
/static/ed2625c1b1c22922dae239bda5490ba2/644c5/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 1440w,
/static/ed2625c1b1c22922dae239bda5490ba2/0f98f/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 1920w,
/static/ed2625c1b1c22922dae239bda5490ba2/34601/01-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 2656w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7beeb5b6aa2673b2546fd7344167b2b6/34601/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGWt05AQv8A/8QAFxAAAwEAAAAAAAAAAAAAAAAAABARAf/aAAgBAQABBQIqmP8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAEDH/2gAIAQEABj8CY//EABsQAAEEAwAAAAAAAAAAAAAAAAEAEBExQWGR/9oACAEBAAE/IZMYRNtTiNlv/9oADAMBAAIAAwAAABCMz//EABYRAQEBAAAAAAAAAAAAAAAAAAEQEf/aAAgBAwEBPxAdn//EABYRAQEBAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxBMn//EAB0QAAIBBAMAAAAAAAAAAAAAAAABERAhMaFBcZH/2gAIAQEAAT8QtMF2QuPRuXJlWsbTp//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;an error occurred while installing pg (1.5.6), and Bundler cannot continue.&quot;
        title=&quot;an error occurred while installing pg (1.5.6), and Bundler cannot continue.&quot;
        src=&quot;/static/7beeb5b6aa2673b2546fd7344167b2b6/6a068/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg&quot;
        srcset=&quot;/static/7beeb5b6aa2673b2546fd7344167b2b6/09b79/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 240w,
/static/7beeb5b6aa2673b2546fd7344167b2b6/7cc5e/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 480w,
/static/7beeb5b6aa2673b2546fd7344167b2b6/6a068/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 960w,
/static/7beeb5b6aa2673b2546fd7344167b2b6/644c5/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 1440w,
/static/7beeb5b6aa2673b2546fd7344167b2b6/0f98f/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 1920w,
/static/7beeb5b6aa2673b2546fd7344167b2b6/34601/02-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 2656w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;Based on the error message, it is necessary to install &lt;code class=&quot;language-text&quot;&gt;libpq&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Please install libpq or postgresql client package like so:
  brew install libpq&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Follow these steps to resolve the issue:&lt;/p&gt;
&lt;h3 id=&quot;step-1-install-libpq&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-install-libpq&quot; aria-label=&quot;step 1 install libpq permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1. Install &lt;code class=&quot;language-text&quot;&gt;libpq&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To resolve the issue, follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install &lt;code class=&quot;language-text&quot;&gt;libpq&lt;/code&gt; by running the following command:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libpq&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;After successful installation, you will see the following message:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;libpq is keg-only, which means it was not symlinked into /opt/homebrew,
because conflicts with postgres formula.

If you need to have libpq first in your PATH, run:
  echo &apos;export PATH=&quot;/opt/homebrew/opt/libpq/bin:$PATH&quot;&apos; &gt;&gt; ~/.zshrc

For compilers to find libpq you may need to set:
  export LDFLAGS=&quot;-L/opt/homebrew/opt/libpq/lib&quot;
  export CPPFLAGS=&quot;-I/opt/homebrew/opt/libpq/include&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;step-2-add-libpq-to-the-path&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-add-libpq-to-the-path&quot; aria-label=&quot;step 2 add libpq to the path permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2. Add &lt;code class=&quot;language-text&quot;&gt;libpq&lt;/code&gt; to the PATH&lt;/h3&gt;
&lt;p&gt;To add &lt;code class=&quot;language-text&quot;&gt;libpq&lt;/code&gt; to the PATH, you have two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run the following command as suggested above:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;export PATH=&quot;/opt/homebrew/opt/libpq/bin:$PATH&quot;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; ~/.zshrc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Alternatively, you can run the link command:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--force&lt;/span&gt; libpq&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The link command ensures that the library is correctly linked to the path. Without running the link command, the same error will occur.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The link command symlinks the installed package (located in /usr/local/Cellar) to /usr/local.
So when you type, for example:
&lt;code class=&quot;language-text&quot;&gt;$ &amp;lt;name-of-binary&gt;&lt;/code&gt;
in your terminal (e.g., $ rvm), the package installed via brew is executed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;After performing the above two steps, run the &lt;code class=&quot;language-text&quot;&gt;rails new&lt;/code&gt; command again, and it should work.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6cae1b20fe83579e2144c2ce8ef52ceb/34601/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMCBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHlztFDZf/EABUQAQEAAAAAAAAAAAAAAAAAABBB/9oACAEBAAEFAmH/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAXEAEBAQEAAAAAAAAAAAAAAAABABAh/9oACAEBAAE/IU5OJGQv/9oADAMBAAIAAwAAABBnD//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ECf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhMVGBkf/aAAgBAQABPxAM2KnSezffsCmCbQiUBjk//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;rails new music-rails --database=postgresql&quot;
        title=&quot;rails new music-rails --database=postgresql&quot;
        src=&quot;/static/6cae1b20fe83579e2144c2ce8ef52ceb/6a068/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg&quot;
        srcset=&quot;/static/6cae1b20fe83579e2144c2ce8ef52ceb/09b79/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 240w,
/static/6cae1b20fe83579e2144c2ce8ef52ceb/7cc5e/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 480w,
/static/6cae1b20fe83579e2144c2ce8ef52ceb/6a068/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 960w,
/static/6cae1b20fe83579e2144c2ce8ef52ceb/644c5/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 1440w,
/static/6cae1b20fe83579e2144c2ce8ef52ceb/0f98f/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 1920w,
/static/6cae1b20fe83579e2144c2ce8ef52ceb/34601/03-an-error-occurred-while-installing-pg-and-bundler-cannot-continue.jpg 2656w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Copy Code with Syntax Highlighting from VSCode to PowerPoint]]></title><link>https://trungvose.comvscode-to-powerpoint/</link><guid isPermaLink="false">https://trungvose.comvscode-to-powerpoint/</guid><pubDate>Fri, 19 Jul 2024 14:40:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;When preparing presentations with code snippets, I often encounter two annoying issues when copying code from VSCode to PowerPoint:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The copied code includes the background color from VSCode.&lt;/li&gt;
&lt;li&gt;The indentation of the code is not preserved.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To better understand the problem, refer to the following images:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/c36dbde2063769e955ce3a9f67f8aa52/01-copy-code-from-vscode-to-powerpoint.gif&quot; alt=&quot;Copy Code from VSCode to PowerPoint&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/4ca94/02-copy-code-from-vscode-to-powerpoint.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAYbYHGCQ/8QAGhAAAwEBAQEAAAAAAAAAAAAAAQIDAAQhIv/aAAgBAQABBQL5OjFX3UolUeYdFRnY0P8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHhAAAQQBBQAAAAAAAAAAAAAAAAECESESMTJBcqH/2gAIAQEABj8C28Eq2uxi3SCineEvtT//xAAbEAACAwADAAAAAAAAAAAAAAABEQAhMUFRYf/aAAgBAQABPyEMCwTvYAATdWEeU0GzCd6ghIHgR1uBz//aAAwDAQACAAMAAAAQ49//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxBX/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QR//EAB0QAQEAAgIDAQAAAAAAAAAAAAERACFBcTFhkcH/2gAIAQEAAT8QYCFltukfWLreBbom8m95ga3F0RE2CfHIplwP8yrsymtHWf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Copy Code from VSCode to PowerPoint&quot;
        title=&quot;Copy Code from VSCode to PowerPoint&quot;
        src=&quot;/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/6a068/02-copy-code-from-vscode-to-powerpoint.jpg&quot;
        srcset=&quot;/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/09b79/02-copy-code-from-vscode-to-powerpoint.jpg 240w,
/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/7cc5e/02-copy-code-from-vscode-to-powerpoint.jpg 480w,
/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/6a068/02-copy-code-from-vscode-to-powerpoint.jpg 960w,
/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/644c5/02-copy-code-from-vscode-to-powerpoint.jpg 1440w,
/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/0f98f/02-copy-code-from-vscode-to-powerpoint.jpg 1920w,
/static/72eb4cbb8ff5a5f2fb8ac2be40ccbe6c/4ca94/02-copy-code-from-vscode-to-powerpoint.jpg 2548w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;solutions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solutions&quot; aria-label=&quot;solutions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solutions&lt;/h2&gt;
&lt;p&gt;Let’s address each problem individually:&lt;/p&gt;
&lt;h3 id=&quot;1-remove-background-color&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-remove-background-color&quot; aria-label=&quot;1 remove background color permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Remove background color&lt;/h3&gt;
&lt;p&gt;To remove the background color, simply copy the code from VSCode to PowerPoint, select the code, and set the &lt;code class=&quot;language-text&quot;&gt;Text Highlight Color&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;No Colour&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/39087847dcee174882174e7573f55ab8/03-copy-code-from-vscode-to-powerpoint.gif&quot; alt=&quot;Remove Background Color&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-preserve-indentation-tab-space&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-preserve-indentation-tab-space&quot; aria-label=&quot;2 preserve indentation tab space permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Preserve indentation (tab, space)&lt;/h3&gt;
&lt;p&gt;Preserving indentation can be a bit tricky. Although I couldn’t find a direct way to copy code with preserved indentation, I discovered a solution on GitHub. According to &lt;a href=&quot;https://github.com/microsoft/vscode/issues/31829#issuecomment-592138395&quot;&gt;vscode/issues/31829#issuecomment-592138395&lt;/a&gt;, it used to be possible to preserve indentation in VSCode version 1.39.2, but it stopped working in later versions.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7d7c85d1ccee53226078f0dd99e7fc47/b8810/04-copy-code-from-vscode-to-powerpoint.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAARABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAceJYQBEFf/EABYQAQEBAAAAAAAAAAAAAAAAAEEQIP/aAAgBAQABBQIhn//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABYQAAMAAAAAAAAAAAAAAAAAAAAwMf/aAAgBAQAGPwIiP//EABoQAAICAwAAAAAAAAAAAAAAAAARASBhcYH/2gAIAQEAAT8hidhjgw8Dp//aAAwDAQACAAMAAAAQhND9/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPxAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPxAf/8QAHBABAAICAwEAAAAAAAAAAAAAAQBBESEQUaFx/9oACAEBAAE/ECxUVEtoqukfYYC5l29m9eyjgn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Preserve Indentation&quot;
        title=&quot;Preserve Indentation&quot;
        src=&quot;/static/7d7c85d1ccee53226078f0dd99e7fc47/6a068/04-copy-code-from-vscode-to-powerpoint.jpg&quot;
        srcset=&quot;/static/7d7c85d1ccee53226078f0dd99e7fc47/09b79/04-copy-code-from-vscode-to-powerpoint.jpg 240w,
/static/7d7c85d1ccee53226078f0dd99e7fc47/7cc5e/04-copy-code-from-vscode-to-powerpoint.jpg 480w,
/static/7d7c85d1ccee53226078f0dd99e7fc47/6a068/04-copy-code-from-vscode-to-powerpoint.jpg 960w,
/static/7d7c85d1ccee53226078f0dd99e7fc47/644c5/04-copy-code-from-vscode-to-powerpoint.jpg 1440w,
/static/7d7c85d1ccee53226078f0dd99e7fc47/0f98f/04-copy-code-from-vscode-to-powerpoint.jpg 1920w,
/static/7d7c85d1ccee53226078f0dd99e7fc47/b8810/04-copy-code-from-vscode-to-powerpoint.jpg 2344w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To overcome this, you can download &lt;a href=&quot;https://code.visualstudio.com/updates/v1_39&quot;&gt;VSCode 1.39.2&lt;/a&gt; and use it specifically when you need to copy code with preserved indentation to PowerPoint. Even on a Mac with an M2 chip, selecting the VSCode Intel version should work fine.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/00c014097930b6499c3cd20a20f76d05/05-copy-code-from-vscode-to-powerpoint.gif&quot; alt=&quot;Preserve Indentation&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;So, the next time you need to copy code from VSCode to PowerPoint, remember these two simple solutions to make your life easier.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Notion: Rounding to 2 Decimal Places]]></title><link>https://trungvose.comnotion-round-decimal/</link><guid isPermaLink="false">https://trungvose.comnotion-round-decimal/</guid><pubDate>Tue, 02 Jul 2024 14:32:00 GMT</pubDate><content:encoded>&lt;p&gt;Notion is an exceptional tool that I have been using for quite some time. Its underlying database structure, which resembles a table, offers customization options with various views such as gallery, board, list, and calendar.&lt;/p&gt;
&lt;p&gt;Recently, I created a simple database to track my team’s sprint progress on a weekly basis. The database includes fields for the sprint name, date, number of closed tickets, number of open tickets, and a formula column to automatically calculate the percentage of closed tickets.&lt;/p&gt;
&lt;p&gt;The formula is straightforward: &lt;code class=&quot;language-text&quot;&gt;closed tickets / (closed tickets + open tickets)&lt;/code&gt;. In Notion, you can directly reference column names in formulas. For example, I have a formula like &lt;code class=&quot;language-text&quot;&gt;&quot;Complete&quot; / (&quot;Complete&quot; + &quot;Open&quot;)&lt;/code&gt;, which is represented as &lt;code class=&quot;language-text&quot;&gt;prop(&quot;Complete&quot;) / (prop(&quot;Complete&quot;) + prop(&quot;Open&quot;))&lt;/code&gt; when copied and pasted.&lt;/p&gt;
&lt;p&gt;To visualize the result, I have included screenshots below:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b06d9c05183d0b315d0408e354d84d4/d7bef/notion-round-decimal-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.58333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABx00Af//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAQEAAwEAAAAAAAAAAAAAAAABITFxgf/aAAgBAQABPyHdZnHiJVf/2gAMAwEAAgADAAAAEGMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITFBUf/aAAgBAQABPxDPIz5ARoFeX2NZdGKnIigs6n//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Notion formula column&quot;
        title=&quot;Notion formula column&quot;
        src=&quot;/static/9b06d9c05183d0b315d0408e354d84d4/6a068/notion-round-decimal-01.jpg&quot;
        srcset=&quot;/static/9b06d9c05183d0b315d0408e354d84d4/09b79/notion-round-decimal-01.jpg 240w,
/static/9b06d9c05183d0b315d0408e354d84d4/7cc5e/notion-round-decimal-01.jpg 480w,
/static/9b06d9c05183d0b315d0408e354d84d4/6a068/notion-round-decimal-01.jpg 960w,
/static/9b06d9c05183d0b315d0408e354d84d4/644c5/notion-round-decimal-01.jpg 1440w,
/static/9b06d9c05183d0b315d0408e354d84d4/0f98f/notion-round-decimal-01.jpg 1920w,
/static/9b06d9c05183d0b315d0408e354d84d4/d7bef/notion-round-decimal-01.jpg 3084w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the result is a long decimal number. To round it to two decimal places, we can use a workaround similar to rounding numbers in JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1cc0a388d129acf591caeeaccb7775dd/d4eda/notion-round-decimal-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHKWAcrD//EABgQAAIDAAAAAAAAAAAAAAAAAAABESAx/9oACAEBAAEFAnhFP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAMAAwAAAAAAAAAAAAAAAAEQEQAhQf/aAAgBAQABPyEjG1Gdf//aAAwDAQACAAMAAAAQIN//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxBX/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQAxEBEhYZH/2gAIAQEAAT8Q5QndjAFqPd7G2Cf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Notion formula column&quot;
        title=&quot;Notion formula column&quot;
        src=&quot;/static/1cc0a388d129acf591caeeaccb7775dd/6a068/notion-round-decimal-02.jpg&quot;
        srcset=&quot;/static/1cc0a388d129acf591caeeaccb7775dd/09b79/notion-round-decimal-02.jpg 240w,
/static/1cc0a388d129acf591caeeaccb7775dd/7cc5e/notion-round-decimal-02.jpg 480w,
/static/1cc0a388d129acf591caeeaccb7775dd/6a068/notion-round-decimal-02.jpg 960w,
/static/1cc0a388d129acf591caeeaccb7775dd/644c5/notion-round-decimal-02.jpg 1440w,
/static/1cc0a388d129acf591caeeaccb7775dd/d4eda/notion-round-decimal-02.jpg 1878w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;formula&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#formula&quot; aria-label=&quot;formula permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Formula&lt;/h2&gt;
&lt;p&gt;Here’s the formula to round to two decimal places:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;round(prop(&quot;Some column&quot;) * 100) / 100&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can adjust the number of zeros to round to the desired decimal places. For example, to round to one decimal place, use &lt;code class=&quot;language-text&quot;&gt;10&lt;/code&gt;; to round to three decimal places, use &lt;code class=&quot;language-text&quot;&gt;1000&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is how it appears on the Notion page:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5e9e610db91192abf7e616f7fcc15a0b/89a8f/notion-round-decimal-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeTmioP/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAEAAgMAAAAAAAAAAAAAAAABAIERICH/2gAIAQEAAT8homCChxS9P//aAAwDAQACAAMAAAAQ4C//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxBH/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EG1//8QAGhABAAIDAQAAAAAAAAAAAAAAAQARIDFRkf/aAAgBAQABPxCxsfAizqVITgjD/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Notion formula column&quot;
        title=&quot;Notion formula column&quot;
        src=&quot;/static/5e9e610db91192abf7e616f7fcc15a0b/6a068/notion-round-decimal-03.jpg&quot;
        srcset=&quot;/static/5e9e610db91192abf7e616f7fcc15a0b/09b79/notion-round-decimal-03.jpg 240w,
/static/5e9e610db91192abf7e616f7fcc15a0b/7cc5e/notion-round-decimal-03.jpg 480w,
/static/5e9e610db91192abf7e616f7fcc15a0b/6a068/notion-round-decimal-03.jpg 960w,
/static/5e9e610db91192abf7e616f7fcc15a0b/644c5/notion-round-decimal-03.jpg 1440w,
/static/5e9e610db91192abf7e616f7fcc15a0b/0f98f/notion-round-decimal-03.jpg 1920w,
/static/5e9e610db91192abf7e616f7fcc15a0b/89a8f/notion-round-decimal-03.jpg 2216w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;why-does-this-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-does-this-work&quot; aria-label=&quot;why does this work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why does this work?&lt;/h2&gt;
&lt;p&gt;Let’s take an example where the result of the formula is &lt;code class=&quot;language-text&quot;&gt;0.531914893617&lt;/code&gt;. When you multiply it by 100, it becomes &lt;code class=&quot;language-text&quot;&gt;53.1914893617&lt;/code&gt;. Rounding it gives you &lt;code class=&quot;language-text&quot;&gt;53&lt;/code&gt;. Finally, dividing it by 100 gives you &lt;code class=&quot;language-text&quot;&gt;0.53&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here’s the updated result:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4c319c7e36674671f11b6a9a3eb379eb/fc83b/notion-round-decimal-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcioQD//xAAVEAEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAQABBQJv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFxABAAMAAAAAAAAAAAAAAAAAARARMf/aAAgBAQABPyFuXP8A/9oADAMBAAIAAwAAABDzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABgQAQEBAQEAAAAAAAAAAAAAAAERACGh/9oACAEBAAE/EKqSPY6Hszxd/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Notion formula column&quot;
        title=&quot;Notion formula column&quot;
        src=&quot;/static/4c319c7e36674671f11b6a9a3eb379eb/6a068/notion-round-decimal-04.jpg&quot;
        srcset=&quot;/static/4c319c7e36674671f11b6a9a3eb379eb/09b79/notion-round-decimal-04.jpg 240w,
/static/4c319c7e36674671f11b6a9a3eb379eb/7cc5e/notion-round-decimal-04.jpg 480w,
/static/4c319c7e36674671f11b6a9a3eb379eb/6a068/notion-round-decimal-04.jpg 960w,
/static/4c319c7e36674671f11b6a9a3eb379eb/644c5/notion-round-decimal-04.jpg 1440w,
/static/4c319c7e36674671f11b6a9a3eb379eb/fc83b/notion-round-decimal-04.jpg 1556w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding "loading=lazy" to GIF files on my 7-year-old Gatsby blog (with ChatGPT assistance)]]></title><link>https://trungvose.comgatsby-lazy-gif/</link><guid isPermaLink="false">https://trungvose.comgatsby-lazy-gif/</guid><pubDate>Sun, 23 Jun 2024 08:32:00 GMT</pubDate><content:encoded>&lt;p&gt;My blog was built a long time ago using Gatsby. I vaguely remember cloning it from a template, but I can’t recall the exact name.&lt;/p&gt;
&lt;p&gt;Over the years, I have added various features, such as the &lt;a href=&quot;/talks&quot;&gt;talks&lt;/a&gt; page, as I started giving talks.&lt;/p&gt;
&lt;p&gt;The blog was initially built with Gatsby 1 and has been upgraded to Gatsby 2, where it has remained ever since. Although Gatsby 2’s support ended in 2021, I have procrastinated upgrading to the latest version, Gatsby 5. Since my blog is still functional and allows me to add new articles and talks, I haven’t felt a strong urge to perform a complete revamp or refactoring. It has become somewhat of a legacy system that continues to serve its purpose.&lt;/p&gt;
&lt;p&gt;Here is the current &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&quot;dependencies&quot;: {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;@sentry/gatsby&quot;: &quot;^5.29.2&quot;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &quot;gatsby&quot;: &quot;2.32.13&quot;,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-canonical-urls&quot;: &quot;2.9.0&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-catch-links&quot;: &quot;^2.0.4&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-feed&quot;: &quot;^2.0.8&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-google-analytics&quot;: &quot;^2.0.6&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-google-fonts&quot;: &quot;latest&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-next-seo&quot;: &quot;^1.6.1&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-offline&quot;: &quot;^2.0.6&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-react-helmet&quot;: &quot;^3.0.0&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-sass&quot;: &quot;^2.0.1&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-sharp&quot;: &quot;^3.15.0&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-plugin-sitemap&quot;: &quot;^2.0.1&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-remark-autolink-headers&quot;: &quot;^2.1.17&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-remark-copy-linked-files&quot;: &quot;^2.0.5&quot;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &quot;gatsby-remark-images&quot;: &quot;^6.12.1&quot;,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;gatsby-remark-prismjs&quot;: &quot;^3.0.2&quot;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Perhaps one day, I will migrate the blog to Astro, but that remains a future endeavor. As a first step, I plan to migrate all my markdown files to a Notion database and connect my blog to fetch content from Notion instead of using the current markdown files. This change alone would be quite helpful. So, what’s the issue at hand?&lt;/p&gt;
&lt;p&gt;In a &lt;a href=&quot;/blog/image-lazy-load/&quot;&gt;recent article&lt;/a&gt;, I highlighted the awesomeness of the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; property for images. I want to ensure that my blog also benefits from this feature. I recall adding the &lt;code class=&quot;language-text&quot;&gt;loading: &apos;lazy&apos;&lt;/code&gt; option to &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt;, which should apply the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; attribute to all images by default.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;{
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; resolve: &apos;gatsby-transformer-remark&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; options: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   tableOfContents: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     maxDepth: 4,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   plugins: [
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &apos;gatsby-remark-autolink-headers&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &apos;gatsby-remark-lazy-gif&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       resolve: &apos;gatsby-remark-images&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       options: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         maxWidth: 960,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;         loading: &apos;lazy&apos;,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         linkImagesToOriginal: true,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       },&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, upon reviewing my latest article, I noticed that the GIF files do not have the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; property. Consequently, when visiting the page, the GIF files load immediately, resulting in a significant amount of data being loaded, especially for larger GIF files. Since many of my blog posts include GIF files to showcase visual content, this issue needs to be addressed. For example, the &lt;a href=&quot;/blog/angular-view-transitions/&quot;&gt;Angular v17’s View Transitions: Navigate in Elegance&lt;/a&gt; contains several GIF files and at the moment, when you enter the page, all 12 GIF files are loaded immediately, costing about &lt;code class=&quot;language-text&quot;&gt;70mb&lt;/code&gt; of data.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07294fd8c3092565d1254add7e571422/19d82/gatsby-lazy-gif-00.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAQAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABzBSiP//EABcQAQADAAAAAAAAAAAAAAAAAAEAIDH/2gAIAQEAAQUCYbT/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAABBQEAAAAAAAAAAAAAAAABABARITGB/9oACAEBAAE/IcBX4bYQo03/2gAMAwEAAgADAAAAEGQP/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/EKf/xAAaEAEBAQEAAwAAAAAAAAAAAAABABEhQWHB/9oACAEBAAE/ED19kdSZ9kBrxE04cldb/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/07294fd8c3092565d1254add7e571422/6a068/gatsby-lazy-gif-00.jpg&quot;
        srcset=&quot;/static/07294fd8c3092565d1254add7e571422/09b79/gatsby-lazy-gif-00.jpg 240w,
/static/07294fd8c3092565d1254add7e571422/7cc5e/gatsby-lazy-gif-00.jpg 480w,
/static/07294fd8c3092565d1254add7e571422/6a068/gatsby-lazy-gif-00.jpg 960w,
/static/07294fd8c3092565d1254add7e571422/644c5/gatsby-lazy-gif-00.jpg 1440w,
/static/07294fd8c3092565d1254add7e571422/0f98f/gatsby-lazy-gif-00.jpg 1920w,
/static/07294fd8c3092565d1254add7e571422/19d82/gatsby-lazy-gif-00.jpg 3230w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;current-setup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#current-setup&quot; aria-label=&quot;current setup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Current Setup&lt;/h2&gt;
&lt;p&gt;While I have configured &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt; to include the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; property by default for most images, GIF files are not processed by this plugin. For example, the following JPG file has the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; property:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/399ef010ccd8291416bfc1e1a04374c5/5a035/gatsby-lazy-gif-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 78.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHmPVYJx//EABkQAAIDAQAAAAAAAAAAAAAAAAAiAQIQEf/aAAgBAQABBQKuxwUU/8QAFREBAQAAAAAAAAAAAAAAAAAAABL/2gAIAQMBAT8BpT//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEv/aAAgBAgEBPwGUv//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAQABBQAAAAAAAAAAAAAAAAARARAhQfD/2gAIAQEAAT8h2QhSczZ//9oADAMBAAIAAwAAABAIP//EABcRAAMBAAAAAAAAAAAAAAAAAAABURH/2gAIAQMBAT8QSQxD/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAFREf/aAAgBAgEBPxBvTdP/xAAdEAACAgEFAAAAAAAAAAAAAAAAAREhUTFhcZGh/9oACAEBAAE/ELNCGWR36FW5hVk5ISmr9P/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/399ef010ccd8291416bfc1e1a04374c5/6a068/gatsby-lazy-gif-01.jpg&quot;
        srcset=&quot;/static/399ef010ccd8291416bfc1e1a04374c5/09b79/gatsby-lazy-gif-01.jpg 240w,
/static/399ef010ccd8291416bfc1e1a04374c5/7cc5e/gatsby-lazy-gif-01.jpg 480w,
/static/399ef010ccd8291416bfc1e1a04374c5/6a068/gatsby-lazy-gif-01.jpg 960w,
/static/399ef010ccd8291416bfc1e1a04374c5/644c5/gatsby-lazy-gif-01.jpg 1440w,
/static/399ef010ccd8291416bfc1e1a04374c5/0f98f/gatsby-lazy-gif-01.jpg 1920w,
/static/399ef010ccd8291416bfc1e1a04374c5/5a035/gatsby-lazy-gif-01.jpg 2230w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But the GIF file does not have the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; property. The img tag below only has the &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;alt&lt;/code&gt; attributes:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e916a889034fb6e2e8cb99813fed04b1/f2991/gatsby-lazy-gif-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcyQqD//xAAWEAEBAQAAAAAAAAAAAAAAAAAAIQH/2gAIAQEAAQUCxEf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAEEH/2gAIAQEABj8CKv/EABkQAAIDAQAAAAAAAAAAAAAAAAERABAxcf/aAAgBAQABPyFEdoPU/9oADAMBAAIAAwAAABAID//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/EKf/xAAbEAACAgMBAAAAAAAAAAAAAAAAEQFhIVGRof/aAAgBAQABPxCsUh7dDZzPk//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/e916a889034fb6e2e8cb99813fed04b1/6a068/gatsby-lazy-gif-02.jpg&quot;
        srcset=&quot;/static/e916a889034fb6e2e8cb99813fed04b1/09b79/gatsby-lazy-gif-02.jpg 240w,
/static/e916a889034fb6e2e8cb99813fed04b1/7cc5e/gatsby-lazy-gif-02.jpg 480w,
/static/e916a889034fb6e2e8cb99813fed04b1/6a068/gatsby-lazy-gif-02.jpg 960w,
/static/e916a889034fb6e2e8cb99813fed04b1/644c5/gatsby-lazy-gif-02.jpg 1440w,
/static/e916a889034fb6e2e8cb99813fed04b1/0f98f/gatsby-lazy-gif-02.jpg 1920w,
/static/e916a889034fb6e2e8cb99813fed04b1/f2991/gatsby-lazy-gif-02.jpg 2222w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/0a94a1f59b4ffa9c061154716bd42de2/angular-view-transitions-07.gif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Customised Transition&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The reason why GIFs are excluded can be found in the &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt; documentation and some related issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-remark-images/#supported-formats&quot;&gt;gatsbyjs.com/plugins/gatsby-remark-images/#supported-formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/7317#issuecomment-412984851&quot;&gt;gatsby-remark-images gif and svg images aren’t recognized/rendered #7317&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bc9f7424a28aaec6b247df1549ed2010/bc4e4/gatsby-lazy-gif-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3aFB/8QAFhAAAwAAAAAAAAAAAAAAAAAAABAR/9oACAEBAAEFAir/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAaEAACAgMAAAAAAAAAAAAAAAABEQAhEEGR/9oACAEBAAE/IaFqBtHmP//aAAwDAQACAAMAAAAQh8//xAAVEQEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAwEBPxCH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRABAAMBAQAAAAAAAAAAAAAAAQARQSHB/9oACAEBAAE/EO7Zd4MD0KDeM//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/bc9f7424a28aaec6b247df1549ed2010/6a068/gatsby-lazy-gif-03.jpg&quot;
        srcset=&quot;/static/bc9f7424a28aaec6b247df1549ed2010/09b79/gatsby-lazy-gif-03.jpg 240w,
/static/bc9f7424a28aaec6b247df1549ed2010/7cc5e/gatsby-lazy-gif-03.jpg 480w,
/static/bc9f7424a28aaec6b247df1549ed2010/6a068/gatsby-lazy-gif-03.jpg 960w,
/static/bc9f7424a28aaec6b247df1549ed2010/644c5/gatsby-lazy-gif-03.jpg 1440w,
/static/bc9f7424a28aaec6b247df1549ed2010/0f98f/gatsby-lazy-gif-03.jpg 1920w,
/static/bc9f7424a28aaec6b247df1549ed2010/bc4e4/gatsby-lazy-gif-03.jpg 2118w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#approach&quot; aria-label=&quot;approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Approach&lt;/h2&gt;
&lt;p&gt;What I want is simple: adding the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; property to GIF files. Since &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt; doesn’t support GIFs, I need to find an alternative solution.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; &amp;lt;img src=&quot;/0a94a1f59b4ffa9c061154716bd42de2/angular-view-transitions-07.gif&quot; alt=&quot;Customised Transition&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &amp;lt;img src=&quot;/0a94a1f59b4ffa9c061154716bd42de2/angular-view-transitions-07.gif&quot; alt=&quot;Customised Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Searching Google doesn’t yield any straightforward solutions.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0c3cd122d2882384305112e18561bf7f/c496e/gatsby-lazy-gif-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAQAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByRAiP//EABUQAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAEFAil//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAEQESEx/9oACAEBAAE/IdkPM5HP/9oADAMBAAIAAwAAABDwz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAICAgMAAAAAAAAAAAAAAAERABAhQTGRof/aAAgBAQABPxBiFltYDiDvqhBJjcPPHtf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/0c3cd122d2882384305112e18561bf7f/6a068/gatsby-lazy-gif-04.jpg&quot;
        srcset=&quot;/static/0c3cd122d2882384305112e18561bf7f/09b79/gatsby-lazy-gif-04.jpg 240w,
/static/0c3cd122d2882384305112e18561bf7f/7cc5e/gatsby-lazy-gif-04.jpg 480w,
/static/0c3cd122d2882384305112e18561bf7f/6a068/gatsby-lazy-gif-04.jpg 960w,
/static/0c3cd122d2882384305112e18561bf7f/644c5/gatsby-lazy-gif-04.jpg 1440w,
/static/0c3cd122d2882384305112e18561bf7f/0f98f/gatsby-lazy-gif-04.jpg 1920w,
/static/0c3cd122d2882384305112e18561bf7f/c496e/gatsby-lazy-gif-04.jpg 2104w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Thus, I decided to ask ChatGPT for help. Here is the conversation:&lt;/p&gt;
&lt;p&gt;See the full conversation &lt;a href=&quot;https://chatgpt.com/share/ff1bcf01-f31a-4096-a0aa-d0a27ba2f985&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d5215e93c41d9d569d22f45edf3caf80/e8d98/gatsby-lazy-gif-07.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.666666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQACBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeSiZiP/xAAXEAADAQAAAAAAAAAAAAAAAAAAARAR/9oACAEBAAEFAjYr/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRABAAMBAQAAAAAAAAAAAAAAAQAQETFR/9oACAEBAAE/ITKLAZyIeV//2gAMAwEAAgADAAAAEDDP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxAAAgIDAQAAAAAAAAAAAAAAAREAIVGBkXH/2gAIAQEAAT8QQAbfsJZOlGFwhiB1AnTkrA5P/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/d5215e93c41d9d569d22f45edf3caf80/6a068/gatsby-lazy-gif-07.jpg&quot;
        srcset=&quot;/static/d5215e93c41d9d569d22f45edf3caf80/09b79/gatsby-lazy-gif-07.jpg 240w,
/static/d5215e93c41d9d569d22f45edf3caf80/7cc5e/gatsby-lazy-gif-07.jpg 480w,
/static/d5215e93c41d9d569d22f45edf3caf80/6a068/gatsby-lazy-gif-07.jpg 960w,
/static/d5215e93c41d9d569d22f45edf3caf80/644c5/gatsby-lazy-gif-07.jpg 1440w,
/static/d5215e93c41d9d569d22f45edf3caf80/e8d98/gatsby-lazy-gif-07.jpg 1642w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;attempt-1-gpt-suggests-updating-gatsby-configjs-for-gatsby-remark-images&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#attempt-1-gpt-suggests-updating-gatsby-configjs-for-gatsby-remark-images&quot; aria-label=&quot;attempt 1 gpt suggests updating gatsby configjs for gatsby remark images permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Attempt 1: GPT suggests updating &lt;code class=&quot;language-text&quot;&gt;gatsby-config.js&lt;/code&gt; for &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Basically, ChatGPT suggested updating the &lt;code class=&quot;language-text&quot;&gt;gatsby-config.js&lt;/code&gt; that utilise &lt;code class=&quot;language-text&quot;&gt;wrapperStyle&lt;/code&gt; from &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt;. This custom transformer will add the &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; attribute to .gif images.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;// gatsby-config.js

module.exports = {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; siteMetadata: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   // Your site metadata
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; plugins: [
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     resolve: &apos;gatsby-transformer-remark&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     options: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       plugins: [
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           resolve: &apos;gatsby-remark-images&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           options: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             maxWidth: 800,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             loading: &apos;lazy&apos;, // Default loading for images
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;              wrapperStyle: (fluidResult) =&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;                fluidResult.src.endsWith(&apos;.gif&apos;) ? &apos;loading=&quot;lazy&quot;&apos; : &apos;&apos;,
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;            },
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       ],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   // Other plugins
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ],
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, as &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt; doesn’t support GIFs, this approach won’t work because the &lt;code class=&quot;language-text&quot;&gt;wrapperStyle&lt;/code&gt; function is only applied to images processed by &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt;. GIFs are not processed by this plugin.&lt;/p&gt;
&lt;p&gt;So I asked for a follow-up, and ChatGPT suggested writing a &lt;code class=&quot;language-text&quot;&gt;custom plugin&lt;/code&gt; to handle GIFs.&lt;/p&gt;
&lt;h3 id=&quot;attempt-2-write-a-custom-plugin-to-handle-gifs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#attempt-2-write-a-custom-plugin-to-handle-gifs&quot; aria-label=&quot;attempt 2 write a custom plugin to handle gifs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Attempt 2: Write a custom plugin to handle GIFs&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7d3d887d90df6f297ae354bf39c6a4a1/25339/gatsby-lazy-gif-08.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHlPNFEh//EABYQAQEBAAAAAAAAAAAAAAAAABARAP/aAAgBAQABBQJup//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAQEBAQEAAAAAAAAAAAAAAAEAESFB/9oACAEBAAE/IQ97LLraJo7f/9oADAMBAAIAAwAAABAgz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQADAAMAAAAAAAAAAAAAAAEAESFRkbH/2gAIAQEAAT8QIoaihdvRNj2GTiMukpXQT//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/7d3d887d90df6f297ae354bf39c6a4a1/6a068/gatsby-lazy-gif-08.jpg&quot;
        srcset=&quot;/static/7d3d887d90df6f297ae354bf39c6a4a1/09b79/gatsby-lazy-gif-08.jpg 240w,
/static/7d3d887d90df6f297ae354bf39c6a4a1/7cc5e/gatsby-lazy-gif-08.jpg 480w,
/static/7d3d887d90df6f297ae354bf39c6a4a1/6a068/gatsby-lazy-gif-08.jpg 960w,
/static/7d3d887d90df6f297ae354bf39c6a4a1/644c5/gatsby-lazy-gif-08.jpg 1440w,
/static/7d3d887d90df6f297ae354bf39c6a4a1/25339/gatsby-lazy-gif-08.jpg 1664w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Below are the steps suggested by ChatGPT:&lt;/p&gt;
&lt;p&gt;If &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt; is filtering out .gif images and you need to add &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; to them, you can use a custom plugin to directly modify the markdown AST before &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt; processes it. Here’s how you can achieve this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the custom plugin directory structure:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; plugins/gatsby-remark-lazy-gif&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Inside &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-lazy-gif&lt;/code&gt;, create an &lt;code class=&quot;language-text&quot;&gt;index.js&lt;/code&gt; file with the following content:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// plugins/gatsby-remark-lazy-gif/index.js&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; visit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;unist-util-visit&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; markdownAST &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;visit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;markdownAST&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;image&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; node &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.gif&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hProperties &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hProperties &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hProperties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loading &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lazy&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; markdownAST&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Update your &lt;code class=&quot;language-text&quot;&gt;gatsby-config.js&lt;/code&gt; to use the custom plugin before &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;// gatsby-config.js

module.exports = {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; siteMetadata: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   // Your site metadata
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; plugins: [
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     resolve: &apos;gatsby-transformer-remark&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     options: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       plugins: [
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;         &apos;gatsby-remark-lazy-gif&apos;, // Add the custom plugin here
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           resolve: &apos;gatsby-remark-images&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           options: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;             maxWidth: 800,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;           },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;         },&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I followed the instructions and created the custom plugin. Then, I updated the &lt;code class=&quot;language-text&quot;&gt;gatsby-config.js&lt;/code&gt; to include the custom plugin before &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt;. However, it threw an error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Plugin gatsby-remark-lazy-gif requires a package.json file&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9140621dc30278db35c4f296333da50e/68a69/gatsby-lazy-gif-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcuHICD/xAAXEAADAQAAAAAAAAAAAAAAAAAQESAh/9oACAEBAAEFAtDj/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAHRAAAgEEAwAAAAAAAAAAAAAAAAFBEBEhUWGRof/aAAgBAQABPyFvl2PEel22OB0//9oADAMBAAIAAwAAABCjz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAEEAwEAAAAAAAAAAAAAAAEAESExEEFhgf/aAAgBAQABPxDQh5QApVCwEBQelUOZf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/9140621dc30278db35c4f296333da50e/6a068/gatsby-lazy-gif-05.jpg&quot;
        srcset=&quot;/static/9140621dc30278db35c4f296333da50e/09b79/gatsby-lazy-gif-05.jpg 240w,
/static/9140621dc30278db35c4f296333da50e/7cc5e/gatsby-lazy-gif-05.jpg 480w,
/static/9140621dc30278db35c4f296333da50e/6a068/gatsby-lazy-gif-05.jpg 960w,
/static/9140621dc30278db35c4f296333da50e/644c5/gatsby-lazy-gif-05.jpg 1440w,
/static/9140621dc30278db35c4f296333da50e/0f98f/gatsby-lazy-gif-05.jpg 1920w,
/static/9140621dc30278db35c4f296333da50e/68a69/gatsby-lazy-gif-05.jpg 2342w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I asked for further guidance, and ChatGPT suggested creating a &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file for the plugin, as seen in the conversation below:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/391cceab7bfb29e4a6582c6369b449bf/f41ad/gatsby-lazy-gif-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 107.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAWABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHkznU0VCoSD//EABYQAQEBAAAAAAAAAAAAAAAAABAhAP/aAAgBAQABBQJurT//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAY/Ah//xAAZEAADAQEBAAAAAAAAAAAAAAAAARFBMVH/2gAIAQEAAT8hS0o+kwQK6NsvoTen/9oADAMBAAIAAwAAABDwADz/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAdEAEAAwACAwEAAAAAAAAAAAABABEhUXFBYZGh/9oACAEBAAE/ECo1+R1z0EdwtbavGxF1fsx0r3ACWnFM92MNWz//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        title=&quot;Adding &amp;quot;loading=lazy&amp;quot; to GIF files on my 7-year-old Gatsby blog&quot;
        src=&quot;/static/391cceab7bfb29e4a6582c6369b449bf/6a068/gatsby-lazy-gif-06.jpg&quot;
        srcset=&quot;/static/391cceab7bfb29e4a6582c6369b449bf/09b79/gatsby-lazy-gif-06.jpg 240w,
/static/391cceab7bfb29e4a6582c6369b449bf/7cc5e/gatsby-lazy-gif-06.jpg 480w,
/static/391cceab7bfb29e4a6582c6369b449bf/6a068/gatsby-lazy-gif-06.jpg 960w,
/static/391cceab7bfb29e4a6582c6369b449bf/644c5/gatsby-lazy-gif-06.jpg 1440w,
/static/391cceab7bfb29e4a6582c6369b449bf/f41ad/gatsby-lazy-gif-06.jpg 1682w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And voila! It works! Now all my GIF files have the &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; property. As you scroll down the page, the GIF files are loaded dynamically, reducing the initial data load.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/dc9d3b4a47906305d9f2f4c80a603ab2/gatsby-lazy-gif-07.gif&quot; alt=&quot;Adding &amp;#x22;loading=lazy&amp;#x22; to GIF files on my 7-year-old Gatsby blog&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;To boost website performance, add &lt;code class=&quot;language-text&quot;&gt;loading=&apos;lazy&apos;&lt;/code&gt; to the &lt;code class=&quot;language-text&quot;&gt;img&lt;/code&gt; tag. While Gatsby’s default image transformer, &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-images&lt;/code&gt;, doesn’t support GIFs, we can create a custom plugin to handle them.&lt;/p&gt;
&lt;p&gt;ChatGPT has been super helpful in creating the plugin and solving this problem. I am a daily use of AI tools such as Copilot, ChatGPT and others and I really appreciate the value they bring to my work. This article was also written with ChatGPT’s help for grammar and suggestions.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular v17’s View Transitions: Navigate in Elegance]]></title><link>https://trungvose.comangular-view-transitions/</link><guid isPermaLink="false">https://trungvose.comangular-view-transitions/</guid><pubDate>Sat, 22 Jun 2024 15:32:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;angular-view-transitions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#angular-view-transitions&quot; aria-label=&quot;angular view transitions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Angular View Transitions&lt;/h2&gt;
&lt;p&gt;As users navigate through your site, they’ll appreciate the smooth and polished transitions that keep them engaged. Angular v17 introduces integrated support for the innovative View Transitions API. This API simplifies DOM changes in a single step while generating an animated transition between the two states. You can explore this feature in depth by following &lt;a href=&quot;https://developer.chrome.com/docs/web-platform/view-transitions/&quot;&gt;Smooth transitions with the View Transition API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Angular’s Router now includes an optional feature that uses the &lt;code class=&quot;language-text&quot;&gt;document.startViewTransition&lt;/code&gt; callback to activate and deactivate route, ensuring smooth transitions when navigating between pages. This feature also gracefully handles browsers that do not support view transitions, providing a consistent navigation experience.&lt;/p&gt;
&lt;p&gt;To enable view transitions in Angular, there are two approaches. If you are using &lt;code class=&quot;language-text&quot;&gt;RouterModule.forRoot&lt;/code&gt;, simply set &lt;code class=&quot;language-text&quot;&gt;enableViewTransitions&lt;/code&gt; to true in the router configuration:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@NgModule({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; imports: [
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   CommonModule,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   WebLayoutModule,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   IconModule,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   NoopAnimationsModule,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   RouterModule.forRoot(webShellRoutes, {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     scrollPositionRestoration: &apos;top&apos;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     enableViewTransitions: true
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   }),
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   StoreModule.forRoot(rootReducers),
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   EffectsModule.forRoot([ApplicationEffects, PlaylistsEffect, PlaylistTracksEffect]),
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   SettingsModule,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   ...extModules&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Alternatively, if you are using &lt;code class=&quot;language-text&quot;&gt;provideRouter&lt;/code&gt;, you can add the &lt;code class=&quot;language-text&quot;&gt;withViewTransitions&lt;/code&gt; option:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@NgModule({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; exports: [RouterModule],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; providers: [
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   provideRouter(
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     webShellRoutes,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     withViewTransitions()
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   )
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ],&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And just like that, pages cross-fade. The screenshots below demonstrate the view transition effect.&lt;/p&gt;
&lt;h3 id=&quot;before&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#before&quot; aria-label=&quot;before permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Before&lt;/h3&gt;
&lt;p&gt;Previously, the page would render immediately when a link was clicked and the route changed&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/c57af62aee2aef3f09381ad7f7998df7/angular-view-transitions-01.gif&quot; alt=&quot;Before&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;after&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#after&quot; aria-label=&quot;after permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;After&lt;/h3&gt;
&lt;p&gt;However, after enabling view transitions, you can see the faded effect during the transition.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/8b83272210b3511d81b6e169856a2c0e/angular-view-transitions-02.gif&quot; alt=&quot;After&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-do-these-transitions-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-do-these-transitions-work&quot; aria-label=&quot;how do these transitions work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How Do These Transitions Work?&lt;/h2&gt;
&lt;h3 id=&quot;angular-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#angular-implementation&quot; aria-label=&quot;angular implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Angular Implementation&lt;/h3&gt;
&lt;p&gt;When you enable view transitions with &lt;code class=&quot;language-text&quot;&gt;enableViewTransitions: true&lt;/code&gt; in Angular, it uses &lt;code class=&quot;language-text&quot;&gt;withViewTransitions&lt;/code&gt; under the hood, as shown in the code snippet below.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/angular/angular/blob/29ca6d10cc3cd75ebdf64658dafcb3ce579af343/packages/router/src/router_module.ts#L157&quot;&gt;packages/router/src/router_module.ts#L157&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders&amp;lt;RouterModule&gt; {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   ngModule: RouterModule,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   providers: [
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      config?.enableViewTransitions ? withViewTransitions().ɵproviders : [],
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   ],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; };&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;withViewTransitions&lt;/code&gt; returns a &lt;code class=&quot;language-text&quot;&gt;routerFeature&lt;/code&gt; with a highlight on &lt;code class=&quot;language-text&quot;&gt;createViewTransition&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;export function withViewTransitions(
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; options?: ViewTransitionsFeatureOptions,
&lt;/span&gt;): ViewTransitionsFeature {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; const providers = [
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    {provide: CREATE_VIEW_TRANSITION, useValue: createViewTransition},
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     provide: VIEW_TRANSITION_OPTIONS,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     useValue: {skipNextTransition: !!options?.skipInitialTransition, ...options},
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   },
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ];
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return routerFeature(RouterFeatureKind.ViewTransitionsFeature, providers);
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;createViewTransition&lt;/code&gt; calls &lt;code class=&quot;language-text&quot;&gt;document.startViewTransition&lt;/code&gt;, but the DOM isn’t updated within the transition callback as usual. Instead, it synchronously activates and deactivates routes during the transition. This is the only place in the Angular codebase where &lt;code class=&quot;language-text&quot;&gt;document.startViewTransition&lt;/code&gt; is used.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/angular/angular/blob/29ca6d10cc3cd75ebdf64658dafcb3ce579af343/packages/router/src/utils/view_transition.ts#L121&quot;&gt;packages/router/src/utils/view_transition/view_transition.ts#L121&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * A helper function for using browser view transitions. This function skips the call to
 * `startViewTransition` if the browser does not support it.
 *
 * @returns A Promise that resolves when the view transition callback begins.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createViewTransition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  injector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Injector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  from&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ActivatedRouteSnapshot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  to&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ActivatedRouteSnapshot
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transitionOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VIEW_TRANSITION_OPTIONS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; document &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DOCUMENT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Create promises outside the Angular zone to avoid causing extra change detections&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;NgZone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;runOutsideAngular&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startViewTransition &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; transitionOptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;skipNextTransition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      transitionOptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;skipNextTransition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// The timing of `startViewTransition` is closer to a macrotask. It won&apos;t be called&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// until the current event loop exits so we use a promise resolved in a timeout instead&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// of Promise.resolve().&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;resolveViewTransitionStarted&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; viewTransitionStarted &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      resolveViewTransitionStarted &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; resolve
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startViewTransition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;resolveViewTransitionStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// We don&apos;t actually update dom within the transition callback. The resolving of the above&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// promise unblocks the Router navigation, which synchronously activates and deactivates&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// routes (the DOM update). This view transition waits for the next change detection to&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// complete (below), which includes the update phase of the routed components.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createRenderPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; onViewTransitionCreated &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transitionOptions
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onViewTransitionCreated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;runInInjectionContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;onViewTransitionCreated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; transition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; to &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; viewTransitionStarted
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In Angular, a single-document view transition is applied for SPAs, as it runs within a single document, animating changes to the DOM when navigating between routes. However, when using server-side rendering (SSR), we may need to incorporate cross-document view transitions.&lt;/p&gt;
&lt;p&gt;To achieve cross-document view transitions, we can utilize the &lt;code class=&quot;language-text&quot;&gt;document.startViewTransition&lt;/code&gt; method and pass a callback that updates the DOM. The transition is considered complete when the callback returns a promise that resolves once the DOM has been fully updated.&lt;/p&gt;
&lt;h3 id=&quot;view-transition-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#view-transition-api&quot; aria-label=&quot;view transition api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;View Transition API&lt;/h3&gt;
&lt;p&gt;To trigger a same-document view transition in JS, we use:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startViewTransition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;updateTheDOMSomehow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startViewTransition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateTheDOMSomehow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When invoked, the browser captures snapshots of all elements with a view-transition-name CSS property. After the callback updates the DOM, it takes snapshots of the new state and constructs a pseudo-element tree like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;view-transition
&lt;span class=&quot;token property&quot;&gt;└─&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;view-transition-group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;token property&quot;&gt;└─&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;view-transition-image-pair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;├─&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;view-transition-old&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;└─&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;view-transition-new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These snapshots transition smoothly from their old to new states, while their content crossfades. The &lt;code class=&quot;language-text&quot;&gt;::view-transition&lt;/code&gt; sits as an overlay on the page, useful for setting a background colour for the transition.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;::view-transition-old(root)&lt;/code&gt; is a screenshot of the old view, and &lt;code class=&quot;language-text&quot;&gt;::view-transition-new(root)&lt;/code&gt; is a live representation of the new view. The old view animates from opacity: 1 to opacity: 0, while the new view animates from opacity: 0 to opacity: 1, creating a crossfade. All animations are performed using CSS, allowing for customisation.&lt;/p&gt;
&lt;h3 id=&quot;how-long-is-the-default-crossfade-transition-animation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-long-is-the-default-crossfade-transition-animation&quot; aria-label=&quot;how long is the default crossfade transition animation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How Long is the Default Crossfade Transition Animation?&lt;/h3&gt;
&lt;p&gt;The default crossfade transition animation in Angular lasts 250ms, according to the W3C documentation. You won’t easily find this information on Google, but it is specified in the &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions/#ua-styles&quot;&gt;5. User Agent Stylesheet&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;:root {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; view-transition-name: root;
&lt;/span&gt;}

:root::view-transition {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; position: fixed;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; inset: 0;
&lt;/span&gt;}

:root::view-transition-group(*) {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; position: absolute;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; top: 0;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; left: 0;
&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; animation-duration: 0.25s;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; animation-fill-mode: both;
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can customise the duration of the crossfade transition using CSS, as explained in the next section.&lt;/p&gt;
&lt;h2 id=&quot;using-css-to-customise-transitions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-css-to-customise-transitions&quot; aria-label=&quot;using css to customise transitions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using CSS to Customise Transitions&lt;/h2&gt;
&lt;p&gt;All view transition pseudo-elements can be targeted with CSS. Since the animations are defined using CSS, you can modify them using existing CSS animation properties. For instance, to slow down the animation to 5 seconds, use the following CSS:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;::view-transition-old(root),
::view-transition-new(root)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This change makes the fade much slower. As shown in the video below, the &lt;code class=&quot;language-text&quot;&gt;::view-transition&lt;/code&gt; appears on top of the page.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/b3103fc429213135fd4fc867e6504a7c/angular-view-transitions-03.gif&quot; alt=&quot;Customised Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;customising-view-transition-old&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#customising-view-transition-old&quot; aria-label=&quot;customising view transition old permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Customising &lt;code class=&quot;language-text&quot;&gt;::view-transition-old&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you want to customise the transition for the old view only, apply changes to the &lt;code class=&quot;language-text&quot;&gt;::view-transition-old(root)&lt;/code&gt; pseudo-element. Note that extending the old view’s duration can cause visual issues, such as a flashing effect when the old view disappears while the new view appears quickly.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;::view-transition-old(root)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/010810a8094757659ece8e1e564a48f9/angular-view-transitions-04.gif&quot; alt=&quot;Customised Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;customising-view-transition-new&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#customising-view-transition-new&quot; aria-label=&quot;customising view transition new permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Customising &lt;code class=&quot;language-text&quot;&gt;::view-transition-new&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Similarly, you can customise the transition for the new view only. This is useful if you want the new view to appear slowly. In this case, the new view will appear in 5 seconds while the old view disappears in 250ms default duration. However it creates a black screen before the new view fully appears.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;::view-transition-new(root)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/9c8664da48b6e96077a4822dfdbc9df4/angular-view-transitions-05.gif&quot; alt=&quot;Customised Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;further-customising-the-transition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#further-customising-the-transition&quot; aria-label=&quot;further customising the transition permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Further Customising the Transition&lt;/h3&gt;
&lt;p&gt;You can customize the animations using CSS instead of using the default fade-in and fade-out animation.
If you’re curious about how the default fade-in animation works, you can read more about it in the &lt;a href=&quot;/blog/view-transition-api-cross-document-navigation/&quot;&gt;-ua-view-transition-fade-in&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To apply a slide effect, you can use the following CSS:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slide-from-right&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;300px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slide-to-left&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;to &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-300px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;view-transition-old&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; slide-to-left 5s ease&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;view-transition-new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; slide-from-right 5s ease&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/4781e60db37b8c8b063aa7d99a55b76d/angular-view-transitions-06.gif&quot; alt=&quot;Customised Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notice that the entire page shifts, causing a double side nav bar effect during the transition. To avoid this, either animate the content only or use a less disruptive animation. The Chrome team provides an example of a &lt;a href=&quot;https://developer.chrome.com/docs/web-platform/view-transitions/same-document#transitioning_elements_dont_need_to_be_the_same_dom_element&quot;&gt;fancy animation&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; fade-in&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; fade-out&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slide-from-right&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;30px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slide-to-left&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-30px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;::view-transition-old(root)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 90ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; both fade-out&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 300ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        0.4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        0.2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        1
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; both slide-to-left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;::view-transition-new(root)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 210ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 90ms both fade-in&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 300ms
      &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0.4&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; both slide-from-right&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/e5d099e238a4f01eaf64f2073ea5a1f2/angular-view-transitions-07.gif&quot; alt=&quot;Customised Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;transitioning-multiple-elements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transitioning-multiple-elements&quot; aria-label=&quot;transitioning multiple elements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Transitioning Multiple Elements&lt;/h2&gt;
&lt;p&gt;To avoid transitioning the entire page, you can animate specific elements separately by assigning a &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; to the element.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.media-cover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;view-transition-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media-cover&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; must be unique on the page, following the &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions/#view-transition-name-prop&quot;&gt;specs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/99568e993e4e70af4d8c824c3523e492/377b2/angular-view-transitions-11.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAZABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB4k0WZjJdU5IEH//EABsQAQACAgMAAAAAAAAAAAAAAAEAEBESITFB/9oACAEBAAEFAr8wRgcaxMQ6a//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABYQAAMAAAAAAAAAAAAAAAAAACAwMf/aAAgBAQAGPwIKj//EABsQAAIDAQEBAAAAAAAAAAAAAAABESExEEFx/9oACAEBAAE/IcS+cYlKX5wapyUhiQYGxn//2gAMAwEAAgADAAAAEC8lPP/EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8QH//EABYRAAMAAAAAAAAAAAAAAAAAAAERIP/aAAgBAgEBPxBiP//EAB8QAAMAAgEFAQAAAAAAAAAAAAABETFRcSFBodHh8P/aAAgBAQABPxBdVOwr2ZiKJ4fT8UvYqUwbLcWRj2Li27NHjIxcGZ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Customised Transition&quot;
        title=&quot;Customised Transition&quot;
        src=&quot;/static/99568e993e4e70af4d8c824c3523e492/6a068/angular-view-transitions-11.jpg&quot;
        srcset=&quot;/static/99568e993e4e70af4d8c824c3523e492/09b79/angular-view-transitions-11.jpg 240w,
/static/99568e993e4e70af4d8c824c3523e492/7cc5e/angular-view-transitions-11.jpg 480w,
/static/99568e993e4e70af4d8c824c3523e492/6a068/angular-view-transitions-11.jpg 960w,
/static/99568e993e4e70af4d8c824c3523e492/644c5/angular-view-transitions-11.jpg 1440w,
/static/99568e993e4e70af4d8c824c3523e492/0f98f/angular-view-transitions-11.jpg 1920w,
/static/99568e993e4e70af4d8c824c3523e492/377b2/angular-view-transitions-11.jpg 1980w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For example, on a listing page with 10 playlists, each displayed with an ID from 1 to 10, you can’t use the same &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; for every playlist cover. Here are two ways to handle this unique name issue:&lt;/p&gt;
&lt;h3 id=&quot;1-set-each-elements-name-dynamically&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-set-each-elements-name-dynamically&quot; aria-label=&quot;1 set each elements name dynamically permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Set Each Element’s Name Dynamically&lt;/h3&gt;
&lt;p&gt;For example, on the listing page, assign the name based on the playlist ID.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@for (playlist of playlists.items; track playlist) {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &amp;lt;as-card
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   [style.view-transition-name]=&quot;&apos;media-cover&apos; + playlist.id&quot;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   [title]=&quot;playlist.name&quot;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   [uri]=&quot;playlist.uri&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This way, each playlist cover on the listing page will have a unique name. On the detail page, there will only be a single media cover displayed with the name &lt;code class=&quot;language-text&quot;&gt;media-cover-1&lt;/code&gt;, for example.&lt;/p&gt;
&lt;h3 id=&quot;2-assign-the-name-dynamically-during-the-transition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-assign-the-name-dynamically-during-the-transition&quot; aria-label=&quot;2 assign the name dynamically during the transition permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Assign the Name Dynamically During the Transition&lt;/h3&gt;
&lt;p&gt;Alternatively, when the user clicks on an playlist cover, the name will be assigned dynamically. For example, by default, no cover on the listing page will have &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt;. When an playlist is clicked, the name &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt; will be assigned to the current active playlist cover, making the transition apply only to the active cover.&lt;/p&gt;
&lt;p&gt;The pseudo-code looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;thumbnail&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onclick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  thumbnail&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewTransitionName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;media-cover&apos;&lt;/span&gt;

  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startViewTransition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    thumbnail&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;viewTransitionName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;updateTheDOMSomehow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;using-onviewtransitioncreated-in-angular&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-onviewtransitioncreated-in-angular&quot; aria-label=&quot;using onviewtransitioncreated in angular permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using &lt;code class=&quot;language-text&quot;&gt;onViewTransitionCreated&lt;/code&gt; in Angular&lt;/h3&gt;
&lt;p&gt;We will try to implement the second approach in Angular.&lt;/p&gt;
&lt;p&gt;Angular’s &lt;code class=&quot;language-text&quot;&gt;withViewTransitions&lt;/code&gt; feature can be used with an options object that includes an &lt;code class=&quot;language-text&quot;&gt;onViewTransitionCreated&lt;/code&gt; callback. This callback runs in an &lt;a href=&quot;https://angular.dev/guide/dependency-injection-injectioncontext&quot;&gt;injection context&lt;/a&gt; and receives a &lt;a href=&quot;https://angular.dev/api/router/ViewTransitionInfo&quot;&gt;ViewTransitionInfo object&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s an example using &lt;code class=&quot;language-text&quot;&gt;onViewTransitionCreated&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;bootstrapApplication(App, {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; providers: [
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   provideRouter(
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     routes,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     withComponentInputBinding(),
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     withViewTransitions({ onViewTransitionCreated })
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   ),
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ],
&lt;/span&gt;})&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onViewTransitionCreated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;info&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ViewTransitionInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; currentTransitionService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;CurrentTransitionService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  currentTransitionService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTransition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  info&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;finished&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    currentTransitionService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTransition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Injectable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  providedIn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CurrentTransitionService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; currentTransition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ViewTransitionInfo &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you have the currentTransition object, which contains the &lt;code class=&quot;language-text&quot;&gt;from&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;to&lt;/code&gt; routes. You can use this information to dynamically assign the &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; to the element you want to transition.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@for (playlist of playlists.items; track playlist) {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &amp;lt;as-card
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   [ngClass]=&quot;viewTransitionName(playlist)&quot;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   [title]=&quot;playlist.name&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;viewTransitionName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SpotifyApi&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PlaylistObjectSimplified&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transitionService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentTransition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transitionAlbumId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getViewTransitionParamValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    transition&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    RouterUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Configuration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PlaylistId
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; withViewTransition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transitionAlbumId &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; withViewTransition &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;with-view-transition&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I used the &lt;code class=&quot;language-text&quot;&gt;with-view-transition&lt;/code&gt; class on the parent element of the &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt; component instead of directly on the &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt; component itself. This allows me to reuse the &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt; component in multiple places without applying the view transition directly to it.&lt;/p&gt;
&lt;p&gt;I learn this approach from &lt;a href=&quot;https://github.com/Charca/view-transitions-live/blob/4416f68b6314191ead9a3b0acbd2edd4dec62c69/src/styles/styles.css#L1&quot;&gt;github.com/Charca/view-transitions-live/src/styles/styles.css#L1&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.with-view-transition as-media-cover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;view-transition-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media-cover&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The complex part is &lt;code class=&quot;language-text&quot;&gt;getViewTransitionParamValue&lt;/code&gt;, considering how Angular Spotify is structured. Here’s the relevant code comment:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*
  The reason for accessing the third firstChild is due to the route structure. For example, when rendering AlbumComponent:
  AppComponent
    LayoutComponent
      /album (lazy loadChildren)
        /:albumId (lazy loadChildren)
  Thus, when receiving transition.from or transition.to, only the AppComponent route snapshot is received.
  Therefore, accessing transition?.from.firstChild?.firstChild?.firstChild allows accessing the params.
  In this case, the albumId param is available.
*/&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getViewTransitionParamValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  transition&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ViewTransitionInfo &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  param&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;transition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    transition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;from&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstChild&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;firstChild&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;firstChild&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;paramMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
    transition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstChild&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;firstChild&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;firstChild&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;paramMap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more details, see my &lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/97&quot;&gt;PR #97 - View Transition Playground&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#result&quot; aria-label=&quot;result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Result&lt;/h3&gt;
&lt;p&gt;Regardless of whether you choose to set the name dynamically or during the transition, the outcome remains the same: only the active album cover will transition, while the others remain static.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/1fd7cce16f210ea81e687c866dd54a6a/angular-view-transitions-08.gif&quot; alt=&quot;Customised Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Even if JavaScript is disabled in the browser, the transition will still work as expected since it relies on the browser’s built-in capabilities.&lt;/p&gt;
&lt;p&gt;When debugging with the animation panel, the timeline of &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/75d40/angular-view-transitions-09.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 81.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHmJ0ZFB//EABYQAAMAAAAAAAAAAAAAAAAAAAEQIP/aAAgBAQABBQKAv//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAMAAwAAAAAAAAAAAAAAAAABERAxQf/aAAgBAQABPyElTeex7P/aAAwDAQACAAMAAAAQQM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAgMBAAAAAAAAAAAAAAABABEQITGB/9oACAEBAAE/EFXrKiXY4Jp7J0n/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Customised Transition&quot;
        title=&quot;Customised Transition&quot;
        src=&quot;/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/6a068/angular-view-transitions-09.jpg&quot;
        srcset=&quot;/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/09b79/angular-view-transitions-09.jpg 240w,
/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/7cc5e/angular-view-transitions-09.jpg 480w,
/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/6a068/angular-view-transitions-09.jpg 960w,
/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/644c5/angular-view-transitions-09.jpg 1440w,
/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/0f98f/angular-view-transitions-09.jpg 1920w,
/static/96cafb4d9f2bcba3cf50b2bfb6ca0447/75d40/angular-view-transitions-09.jpg 2182w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notice the new &lt;code class=&quot;language-text&quot;&gt;::view-transition-group&lt;/code&gt; with the name &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt;, which only contains the active album cover, and the built-in animation name &lt;code class=&quot;language-text&quot;&gt;ua-mix-blend-mode-plus-lighter&lt;/code&gt; defined in the &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions/#ua-styles&quot;&gt;W3C documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;According to &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions/#setup-transition-pseudo-elements-algorithm&quot;&gt;7.3.3. Setup transition pseudo-elements&lt;/a&gt; in the documentation, the above animation in the timeline follows the algorithm steps precisely. You’ll see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step 6: The &lt;code class=&quot;language-text&quot;&gt;-ua-view-transition-group-anim-media-cover&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Step 8: &lt;code class=&quot;language-text&quot;&gt;-ua-view-transition-fade-out&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;-ua-mix-blend-mode-plus-lighter&lt;/code&gt; for &lt;code class=&quot;language-text&quot;&gt;::view-transition-old(media-cover)&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;::view-transition-new(media-cover)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As we observed, &lt;code class=&quot;language-text&quot;&gt;::view-transition-old(media-cover)&lt;/code&gt; captures the media cover moving from the listing page to the detail page, which is a great start. However, we can further enhance the transition by applying a custom animation to the &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt; element. For example, if we add a spin animation to the &lt;code class=&quot;language-text&quot;&gt;media-cover&lt;/code&gt; element, the result will be as follows:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; spin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;0%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;100%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;360deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;::view-transition-new(media-cover)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; spin 500ms&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/a369e28c52b1d52ce61ed9e4d7907797/angular-view-transitions-12.gif&quot; alt=&quot;Customised Spin Transition&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;demo--source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#demo--source-code&quot; aria-label=&quot;demo  source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Demo &amp;#x26; Source code&lt;/h2&gt;
&lt;p&gt;Feel free to give it a ⭐️.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/angular-spotify/pull/97&quot;&gt;trungvose/angular-spotify#97&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;official-w3c-documentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#official-w3c-documentation&quot; aria-label=&quot;official w3c documentation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Official W3C Documentation&lt;/h2&gt;
&lt;p&gt;As I have went through the ocean of resources before publishing this articles, I would the W3C should be still a single source of truth to truly understand the View Transitions API with some niche questions you might have in mind e.g how long is the default animation duration and the like. Here are the links to the official documentation&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For same view transitions, you can refer to the &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions&quot;&gt;CSS View Transitions Module Level 1&lt;/a&gt; documentation&lt;/li&gt;
&lt;li&gt;For cross-document view transitions, you can refer to the &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions-2/&quot;&gt;CSS View Transitions Module Level 2&lt;/a&gt; documentation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.angular.dev/check-out-angulars-support-for-the-view-transitions-api-3937376cfc19&quot;&gt;Check out Angular’s support for the View Transitions API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Angular v17’s View Transitions API is a powerful tool that enables you to create smooth transitions between pages. By enabling view transitions in Angular, you can provide users with a more engaging and polished experience as they navigate through your site. While the Angular implementation is straightforward, there is much more to explore and discover with the View Transitions API. I hope this article has provided you with a solid foundation to dive deeper into this exciting feature.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading="lazy")]]></title><link>https://trungvose.comimage-lazy-load/</link><guid isPermaLink="false">https://trungvose.comimage-lazy-load/</guid><pubDate>Wed, 12 Jun 2024 15:32:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Images are often the largest assets on websites, consuming significant bandwidth. On average, sites send over 5 MB of images on desktop and mobile, making image optimization crucial for improving website performance.&lt;/p&gt;
&lt;p&gt;In my Gatsby blog, the &lt;a href=&quot;/talks&quot;&gt;talks&lt;/a&gt; page contains numerous images, but I haven’t implemented any image optimization techniques. As a result, when the page loads, all the images are loaded at once, totaling almost 50 MB. This negatively impacts user experience and website performance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/97c4255d77577128220a0d3859eb428f/5db06/browser-image-lazy-load-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeY1CKP/xAAYEAACAwAAAAAAAAAAAAAAAAAAEAECMf/aAAgBAQABBQIrqhf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAADAQEAAAAAAAAAAAAAAAAAAREQsf/aAAgBAQABPyFnDGhI6Q//2gAMAwEAAgADAAAAENAP/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8QiP/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EKr/xAAaEAADAQEBAQAAAAAAAAAAAAAAASERMUFR/9oACAEBAAE/EFca+2FYdjp4hLO82YJMP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        title=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        src=&quot;/static/97c4255d77577128220a0d3859eb428f/6a068/browser-image-lazy-load-01.jpg&quot;
        srcset=&quot;/static/97c4255d77577128220a0d3859eb428f/09b79/browser-image-lazy-load-01.jpg 240w,
/static/97c4255d77577128220a0d3859eb428f/7cc5e/browser-image-lazy-load-01.jpg 480w,
/static/97c4255d77577128220a0d3859eb428f/6a068/browser-image-lazy-load-01.jpg 960w,
/static/97c4255d77577128220a0d3859eb428f/644c5/browser-image-lazy-load-01.jpg 1440w,
/static/97c4255d77577128220a0d3859eb428f/0f98f/browser-image-lazy-load-01.jpg 1920w,
/static/97c4255d77577128220a0d3859eb428f/5db06/browser-image-lazy-load-01.jpg 2796w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-loading-attribute&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-loading-attribute&quot; aria-label=&quot;the loading attribute permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The loading attribute&lt;/h2&gt;
&lt;p&gt;To address this issue, we can utilize the &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; attribute on the images. This attribute instructs the browser to load the image only when it enters the viewport. By implementing lazy loading, we can significantly improve the performance of our site with minimal effort.&lt;/p&gt;
&lt;p&gt;Previously, developers had two options for deferring the loading of off-screen images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the Intersection Observer API&lt;/li&gt;
&lt;li&gt;Utilizing scroll, resize, or orientationchange event handlers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While these options allowed for lazy loading, many developers relied on third-party libraries to simplify the implementation.&lt;/p&gt;
&lt;p&gt;With browser-level lazy loading support, there is no longer a need for external libraries. Additionally, browser-level lazy loading ensures that images still load even if the client disables JavaScript, although loading is only deferred when JavaScript is enabled.&lt;/p&gt;
&lt;p&gt;For more information on this attribute, refer to the &lt;a href=&quot;https://web.dev/articles/browser-level-image-lazy-loading&quot;&gt;web.dev&lt;/a&gt; documentation.&lt;/p&gt;
&lt;p&gt;Adding &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; to the image tag is a straightforward process:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-arrow deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;&lt;&lt;/span&gt;img
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; className=&quot;talk-image&quot;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; src={`/img/speaking/${featureImage}.jpg`}
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; loading=&quot;lazy&quot;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; alt={title}
&lt;/span&gt;/&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c40e943ce658c5b133621a8d0f0fd663/2347c/browser-image-lazy-load-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 78.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHlZsII/8QAFRABAQAAAAAAAAAAAAAAAAAAECH/2gAIAQEAAQUCY//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EAB0QAAIBBAMAAAAAAAAAAAAAAAABMREhUaFh4fD/2gAIAQEAAT8hUQOmNlsbIdjp5luT/9oADAMBAAIAAwAAABBz7//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EAB4QAAEDBAMAAAAAAAAAAAAAAAEAESExUXHwYZHB/9oACAEBAAE/EGkg4zdADz1hPeQApieWeIBoDrCe3sv/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        title=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        src=&quot;/static/c40e943ce658c5b133621a8d0f0fd663/6a068/browser-image-lazy-load-02.jpg&quot;
        srcset=&quot;/static/c40e943ce658c5b133621a8d0f0fd663/09b79/browser-image-lazy-load-02.jpg 240w,
/static/c40e943ce658c5b133621a8d0f0fd663/7cc5e/browser-image-lazy-load-02.jpg 480w,
/static/c40e943ce658c5b133621a8d0f0fd663/6a068/browser-image-lazy-load-02.jpg 960w,
/static/c40e943ce658c5b133621a8d0f0fd663/644c5/browser-image-lazy-load-02.jpg 1440w,
/static/c40e943ce658c5b133621a8d0f0fd663/2347c/browser-image-lazy-load-02.jpg 1788w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here are the supported values for the loading attribute:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;lazy&lt;/code&gt;: Defer loading of the resource until it reaches a calculated distance from the viewport. Chromium’s implementation of lazy loading tries to ensure that offscreen images are loaded early enough that they finish loading by the time the user scrolls to them by fetching them well before they become visible in the viewport.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;eager&lt;/code&gt;: Default loading behavior of the browser, which is the same as not including the attribute and means the image is loaded regardless of where it’s located on the page. This is the default, but it can be useful to set explicitly if your tooling automatically adds loading=“lazy” when there’s no explicit value, or if your linter complains if it isn’t explicitly set.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#result&quot; aria-label=&quot;result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Result&lt;/h2&gt;
&lt;p&gt;After adding the &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; attribute to the images, the images are loaded only when they are in the viewport. Notice how the first load, only 2mb worth of images are loaded.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f996d08e487c16a2dc5c7fe6aa7505b1/d8eae/browser-image-lazy-load-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.58333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeZmgg//xAAWEAEBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQEAAQUC2Ij/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAACAgMAAAAAAAAAAAAAAAAAAREgIVFx/9oACAEBAAE/IWb4ThUH/9oADAMBAAIAAwAAABBHz//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EIj/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxCq/8QAGxAAAgIDAQAAAAAAAAAAAAAAAAERcSExofD/2gAIAQEAAT8QeIhLYnhvcihQX4OK3wtw/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        title=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        src=&quot;/static/f996d08e487c16a2dc5c7fe6aa7505b1/6a068/browser-image-lazy-load-03.jpg&quot;
        srcset=&quot;/static/f996d08e487c16a2dc5c7fe6aa7505b1/09b79/browser-image-lazy-load-03.jpg 240w,
/static/f996d08e487c16a2dc5c7fe6aa7505b1/7cc5e/browser-image-lazy-load-03.jpg 480w,
/static/f996d08e487c16a2dc5c7fe6aa7505b1/6a068/browser-image-lazy-load-03.jpg 960w,
/static/f996d08e487c16a2dc5c7fe6aa7505b1/644c5/browser-image-lazy-load-03.jpg 1440w,
/static/f996d08e487c16a2dc5c7fe6aa7505b1/0f98f/browser-image-lazy-load-03.jpg 1920w,
/static/f996d08e487c16a2dc5c7fe6aa7505b1/d8eae/browser-image-lazy-load-03.jpg 2804w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The rest of the images are loaded as you scroll down the page. This reduces the amount of data that needs to be loaded when the page is loaded, which improves the performance of the site.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/82751334019387d308b491dd2b9703c0/browser-image-lazy-load-04.gif&quot; alt=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;#x22;lazy&amp;#x22;)&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;next-step&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#next-step&quot; aria-label=&quot;next step permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Next step&lt;/h2&gt;
&lt;p&gt;Adding &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; is a simple yet effective way to enhance your site’s performance. However, there are additional optimizations you can consider. Since the original images may have a high resolution and file size, you can leverage an image transformation service like Cloudinary to further resize and optimize the images. By adding the &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt; attribute to the image tag, you can specify the desired dimensions of the image. Additionally, you can explore using more efficient image formats like WebP or AVIF. These optimizations can significantly reduce the file size and improve the loading speed of your images.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-arrow deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;&lt;&lt;/span&gt;img
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; className=&quot;talk-image&quot;
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; src={`/img/speaking/${featureImage}.jpg`}  
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; src={`/img/speaking/${featureImage}.jpg?w=200`}
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; loading=&quot;lazy&quot;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; alt={title}
&lt;/span&gt;/&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;can-i-use-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#can-i-use-it&quot; aria-label=&quot;can i use it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Can I use it?&lt;/h2&gt;
&lt;p&gt;As of June 2024, the &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; attribute is supported by all major browsers, including Chrome, Firefox, Safari, and Edge. Firefox has supported it since December 2023 and Safari since March 2023. You can check current browser support on &lt;a href=&quot;https://caniuse.com/loading-lazy-attr&quot;&gt;Can I use&lt;/a&gt;. If a browser doesn’t support it yet and you haven’t implemented other lazy load methods, it simply won’t have any effect.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fb673b019aea1520b69e2a564fa5c826/8c44a/browser-image-lazy-load-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAActbFyoSR//EABcQAQADAAAAAAAAAAAAAAAAABABEUH/2gAIAQEAAQUCymT/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAVEAEBAAAAAAAAAAAAAAAAAAAgQf/aAAgBAQAGPwKL/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEQESEx/9oACAEBAAE/Iaeg9FC4oM//2gAMAwEAAgADAAAAEJA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAAIDAAMAAAAAAAAAAAAAAQARITFRQZGh/9oACAEBAAE/EEc2nS5TteLwkV1+Smo0eIDh6gL0T//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        title=&quot;Improving Website Performance with Browser-Level Image Lazy Loading (simply add loading=&amp;quot;lazy&amp;quot;)&quot;
        src=&quot;/static/fb673b019aea1520b69e2a564fa5c826/6a068/browser-image-lazy-load-05.jpg&quot;
        srcset=&quot;/static/fb673b019aea1520b69e2a564fa5c826/09b79/browser-image-lazy-load-05.jpg 240w,
/static/fb673b019aea1520b69e2a564fa5c826/7cc5e/browser-image-lazy-load-05.jpg 480w,
/static/fb673b019aea1520b69e2a564fa5c826/6a068/browser-image-lazy-load-05.jpg 960w,
/static/fb673b019aea1520b69e2a564fa5c826/644c5/browser-image-lazy-load-05.jpg 1440w,
/static/fb673b019aea1520b69e2a564fa5c826/0f98f/browser-image-lazy-load-05.jpg 1920w,
/static/fb673b019aea1520b69e2a564fa5c826/8c44a/browser-image-lazy-load-05.jpg 2868w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Improve Cumulative Layout Shift (CLS)]]></title><description><![CDATA[In this article, we will explore how to improve Cumulative Layout Shift (CLS).]]></description><link>https://trungvose.comweb-performance-improve-cls/</link><guid isPermaLink="false">https://trungvose.comweb-performance-improve-cls/</guid><pubDate>Thu, 16 May 2024 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;It was really busy in Mar and Apr as we welcome our new family member to the world. I am back to the world of Web Performance now. In this article, we will explore how to improve Cumulative Layout Shift (CLS), after we covered &lt;a href=&quot;/blog/web-performance-improve-fcp&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; and &lt;a href=&quot;/blog/web-performance-improve-lcp&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; in the previous articles.&lt;/p&gt;
&lt;p&gt;We did mentioned the basic of CLS in the &lt;a href=&quot;/blog/core-web-vitals&quot;&gt;Core Web Vitals&lt;/a&gt; article. CLS measures the visual stability of a page. It is the sum of all individual layout shift scores for every unexpected layout shift that occurs during the entire lifespan of the page. A layout shift occurs any time a visible element changes its position from one rendered frame to the next. The score is calculated by multiplying the impact fraction by the distance fraction. Let’s look at how we calculate the CLS score.&lt;/p&gt;
&lt;h2 id=&quot;cls-score-calculation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cls-score-calculation&quot; aria-label=&quot;cls score calculation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CLS Score Calculation&lt;/h2&gt;
&lt;p&gt;To calculate the layout shift score, the browser looks at the viewport size and the movement of unstable elements in the viewport between two rendered frames. The layout shift score is a product of two measures of that movement: the impact fraction and the distance fraction (both defined below).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;layout shift score = impact fraction * distance fraction&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;impact fraction: the area of the viewport affected by the layout shift (impact size / viewport size)&lt;/li&gt;
&lt;li&gt;distance fraction: the distance the unstable element moved in the viewport (distance / viewport size)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;impact-fraction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#impact-fraction&quot; aria-label=&quot;impact fraction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Impact fraction&lt;/h3&gt;
&lt;p&gt;The impact fraction measures how unstable elements impact the viewport area between two frames.&lt;/p&gt;
&lt;p&gt;The impact fraction for a given frame is a combination of the visible areas of all unstable elements for that frame and the previous frame, as a fraction of the total area of the viewport.&lt;/p&gt;
&lt;p&gt;In this case, the &lt;code class=&quot;language-text&quot;&gt;impact size = 700&lt;/code&gt; and the &lt;code class=&quot;language-text&quot;&gt;view port size = 936&lt;/code&gt;. The impact fraction = &lt;code class=&quot;language-text&quot;&gt;700 / 936 ~ 0.75&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8aecdfd5080013ef33b1f9a658414878/b17f8/impact-fraction.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHQZ1LyQ//EABgQAQADAQAAAAAAAAAAAAAAAAIAAQMh/9oACAEBAAEFAmKWioKZ8K1oJ3RmfT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAcEAACAQUBAAAAAAAAAAAAAAAAARICESExUUH/2gAIAQEABj8CbaFLOOlloaiymVL14X6f/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARIUFxgf/aAAgBAQABPyFwD9YBW04UJqUWog17lTUHyixCGqZ//9oADAMBAAIAAwAAABDkH//EABURAQEAAAAAAAAAAAAAAAAAAAAB/9oACAEDAQE/EFf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAQADAQEAAAAAAAAAAAABEQAhMVFBYf/aAAgBAQABPxB9HQoR0eOQY6EEftic9aEQEFWF9c6JWHiaHqaGsgK4J3amIUIiFC/m/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Impact fraction&quot;
        title=&quot;Impact fraction&quot;
        src=&quot;/static/8aecdfd5080013ef33b1f9a658414878/6a068/impact-fraction.jpg&quot;
        srcset=&quot;/static/8aecdfd5080013ef33b1f9a658414878/09b79/impact-fraction.jpg 240w,
/static/8aecdfd5080013ef33b1f9a658414878/7cc5e/impact-fraction.jpg 480w,
/static/8aecdfd5080013ef33b1f9a658414878/6a068/impact-fraction.jpg 960w,
/static/8aecdfd5080013ef33b1f9a658414878/644c5/impact-fraction.jpg 1440w,
/static/8aecdfd5080013ef33b1f9a658414878/b17f8/impact-fraction.jpg 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;distance-fraction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#distance-fraction&quot; aria-label=&quot;distance fraction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Distance fraction&lt;/h3&gt;
&lt;p&gt;The other part of the layout shift score equation measures the distance that unstable elements have moved relative to the viewport. The distance fraction is the greatest horizontal or vertical distance any unstable element has moved in the frame divided by the viewport’s largest dimension (width or height, whichever is greater).&lt;/p&gt;
&lt;p&gt;In this case, the &lt;code class=&quot;language-text&quot;&gt;distance = 234&lt;/code&gt; and the &lt;code class=&quot;language-text&quot;&gt;view port size = 936&lt;/code&gt;. The distance fraction = &lt;code class=&quot;language-text&quot;&gt;234 / 936 = 0.25&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The the layout shift score = &lt;code class=&quot;language-text&quot;&gt;(700 / 936) * (234 / 936) = 0.75 * 0.25 = 0.1875&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8175b2e67e641370c01da1074fce8513/b17f8/distance-fraction.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd7UUuAf/8QAGRABAAMBAQAAAAAAAAAAAAAAAgABAyEy/9oACAEBAAEFAtBS0VFTPytaCd0Zn0//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAcEAACAgIDAAAAAAAAAAAAAAAAAQIhEjERQVH/2gAIAQEABj8CdMjldelaGnFkcovXRyf/xAAbEAEAAwEAAwAAAAAAAAAAAAABABEhQTFxgf/aAAgBAQABPyFQU+swlrwoQr4FrYgz3KmsPqiKQgt0z//aAAwDAQACAAMAAAAQ5N//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAAMBAQEAAAAAAAAAAAABEQAhMVFBYf/aAAgBAQABPxDa+QoRw8cKUJCCP2xN8yW0CKmF9cdArDqRD1MgrICtE7amEUKEKF/M/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Distance fraction&quot;
        title=&quot;Distance fraction&quot;
        src=&quot;/static/8175b2e67e641370c01da1074fce8513/6a068/distance-fraction.jpg&quot;
        srcset=&quot;/static/8175b2e67e641370c01da1074fce8513/09b79/distance-fraction.jpg 240w,
/static/8175b2e67e641370c01da1074fce8513/7cc5e/distance-fraction.jpg 480w,
/static/8175b2e67e641370c01da1074fce8513/6a068/distance-fraction.jpg 960w,
/static/8175b2e67e641370c01da1074fce8513/644c5/distance-fraction.jpg 1440w,
/static/8175b2e67e641370c01da1074fce8513/b17f8/distance-fraction.jpg 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example&quot; aria-label=&quot;example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8c0bd0aa5742b3ba4e761d3986ac1504/f149b/cls-combine.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.166666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABgoIsFf/EABcQAQEBAQAAAAAAAAAAAAAAABICEQP/2gAIAQEAAQUCN5T3gx//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAADAQEAAAAAAAAAAAAAAAAAATERof/aAAgBAQAGPwK9Koh61T//xAAaEAACAgMAAAAAAAAAAAAAAAAAAREhMbHB/9oACAEBAAE/IcDYQxo4IcAf/9oADAMBAAIAAwAAABB4D//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/EKf/xAAVEQEBAAAAAAAAAAAAAAAAAAARAP/aAAgBAgEBPxAYv//EABsQAQEAAwADAAAAAAAAAAAAAAERACExUWGR/9oACAEBAAE/EFbFQ7LGUxSXb8vObpG5A5D1n//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cumulative Layout Shift&quot;
        title=&quot;Cumulative Layout Shift&quot;
        src=&quot;/static/8c0bd0aa5742b3ba4e761d3986ac1504/6a068/cls-combine.jpg&quot;
        srcset=&quot;/static/8c0bd0aa5742b3ba4e761d3986ac1504/09b79/cls-combine.jpg 240w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/7cc5e/cls-combine.jpg 480w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/6a068/cls-combine.jpg 960w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/644c5/cls-combine.jpg 1440w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/0f98f/cls-combine.jpg 1920w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/f149b/cls-combine.jpg 3618w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/497bbf179e5c118c9a89c31337a29def/d6f75/cls-layout-shift-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3ViVD//EABYQAAMAAAAAAAAAAAAAAAAAAAAgIf/aAAgBAQABBQIq/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAbEAABBAMAAAAAAAAAAAAAAAAAAREgIVFhgf/aAAgBAQABPyG9nQj5h//aAAwDAQACAAMAAAAQgw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAWEQADAAAAAAAAAAAAAAAAAAABECH/2gAIAQIBAT8QEX//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEhkSBBcf/aAAgBAQABPxBzTaiWnqeEFMrRw//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cumulative Layout Shift&quot;
        title=&quot;Cumulative Layout Shift&quot;
        src=&quot;/static/497bbf179e5c118c9a89c31337a29def/6a068/cls-layout-shift-score.jpg&quot;
        srcset=&quot;/static/497bbf179e5c118c9a89c31337a29def/09b79/cls-layout-shift-score.jpg 240w,
/static/497bbf179e5c118c9a89c31337a29def/7cc5e/cls-layout-shift-score.jpg 480w,
/static/497bbf179e5c118c9a89c31337a29def/6a068/cls-layout-shift-score.jpg 960w,
/static/497bbf179e5c118c9a89c31337a29def/644c5/cls-layout-shift-score.jpg 1440w,
/static/497bbf179e5c118c9a89c31337a29def/0f98f/cls-layout-shift-score.jpg 1920w,
/static/497bbf179e5c118c9a89c31337a29def/d6f75/cls-layout-shift-score.jpg 8000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Another example is the layout shift score for a mobile view:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2fe3c0361a755bad936119b2641866ae/2fab2/cls-mobile-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBBAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAdRRMrhBX//EABoQAAIDAQEAAAAAAAAAAAAAAAERAgMSMjP/2gAIAQEAAQUCtepsiHNz2WI1ef8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAgEFAQAAAAAAAAAAAAAAAAECESEiMUFx/9oACAEBAAY/ArSa8I5M3UtGTtwji9cFVH//xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhUTH/2gAIAQEAAT8hqqTwYpea1NLms3WRpJwFkvfqPCtBPjP/2gAMAwEAAgADAAAAEEsv/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qp//EABYRAQEBAAAAAAAAAAAAAAAAAAEAUf/aAAgBAgEBPxBXIv/EABoQAQADAQEBAAAAAAAAAAAAAAEAESExYUH/2gAIAQEAAT8QIwAym2+jEWqPpL3JlkLGi3ftQBQhtwNvdmzuUFVndgNp7UaTXs//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cumulative Layout Shift&quot;
        title=&quot;Cumulative Layout Shift&quot;
        src=&quot;/static/2fe3c0361a755bad936119b2641866ae/6a068/cls-mobile-score.jpg&quot;
        srcset=&quot;/static/2fe3c0361a755bad936119b2641866ae/09b79/cls-mobile-score.jpg 240w,
/static/2fe3c0361a755bad936119b2641866ae/7cc5e/cls-mobile-score.jpg 480w,
/static/2fe3c0361a755bad936119b2641866ae/6a068/cls-mobile-score.jpg 960w,
/static/2fe3c0361a755bad936119b2641866ae/644c5/cls-mobile-score.jpg 1440w,
/static/2fe3c0361a755bad936119b2641866ae/0f98f/cls-mobile-score.jpg 1920w,
/static/2fe3c0361a755bad936119b2641866ae/2fab2/cls-mobile-score.jpg 2644w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-good-cls-score&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-a-good-cls-score&quot; aria-label=&quot;what is a good cls score permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is a good CLS score?&lt;/h2&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have a CLS score of 0.1 or less. To ensure you’re hitting this target for most of your users, a good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6a160a352f98a9d84b1e695734dea3db/222f7/cls-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.666666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAEABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd2AsD//xAAYEAACAwAAAAAAAAAAAAAAAAAAARESQf/aAAgBAQABBQLaiUH/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFn/8QAFxAAAwEAAAAAAAAAAAAAAAAAABBBkf/aAAgBAQAGPwIur//EABkQAAMBAQEAAAAAAAAAAAAAAAABESExgf/aAAgBAQABPyFrrRo3aEE31n//2gAMAwEAAgADAAAAEHPf/8QAFhEAAwAAAAAAAAAAAAAAAAAAAAEx/9oACAEDAQE/EFR0/8QAFhEBAQEAAAAAAAAAAAAAAAAAIQEQ/9oACAECAQE/EFHP/8QAGxABAAICAwAAAAAAAAAAAAAAAQAhETFBUZH/2gAIAQEAAT8QFu2KzUYYa9IIAhQcovrP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cumulative Layout Shift&quot;
        title=&quot;Cumulative Layout Shift&quot;
        src=&quot;/static/6a160a352f98a9d84b1e695734dea3db/6a068/cls-score.jpg&quot;
        srcset=&quot;/static/6a160a352f98a9d84b1e695734dea3db/09b79/cls-score.jpg 240w,
/static/6a160a352f98a9d84b1e695734dea3db/7cc5e/cls-score.jpg 480w,
/static/6a160a352f98a9d84b1e695734dea3db/6a068/cls-score.jpg 960w,
/static/6a160a352f98a9d84b1e695734dea3db/644c5/cls-score.jpg 1440w,
/static/6a160a352f98a9d84b1e695734dea3db/222f7/cls-score.jpg 1870w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-to-improve-cls&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-improve-cls&quot; aria-label=&quot;how to improve cls permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to improve CLS?&lt;/h2&gt;
&lt;p&gt;Layout shifts are caused by elements that move unexpectedly in the viewport. Here are some common causes of layout shifts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Images without specified dimensions&lt;/li&gt;
&lt;li&gt;Ads, embeds, and iframes without specified dimensions&lt;/li&gt;
&lt;li&gt;Dynamically injected content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To improve CLS, follow these steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always add width and height attributes to images, videos, and iframes.&lt;/li&gt;
&lt;li&gt;Reserve space for dynamically injected content using CSS.&lt;/li&gt;
&lt;li&gt;Use absolute positioning for advert banners to prevent layout shifts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;images-without-dimensions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#images-without-dimensions&quot; aria-label=&quot;images without dimensions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Images without dimensions&lt;/h3&gt;
&lt;p&gt;When an image is loaded without specified dimensions, the browser doesn’t know how much space to allocate. Once the image loads, the browser shifts the content to make space, causing a layout shift.&lt;/p&gt;
&lt;p&gt;For example, on my blog’s &lt;a href=&quot;/talks&quot;&gt;Talks&lt;/a&gt; page, the CLS score was &lt;code class=&quot;language-text&quot;&gt;0.09&lt;/code&gt; without specified image dimensions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d9006b2e698bdeed96b67206ccfcec03/cls-score-blog.gif&quot; alt=&quot;Images without dimensions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;After specifying dimensions, the CLS score improved to &lt;code class=&quot;language-text&quot;&gt;0.01&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/9049ed7681409ca553e66192ffb7b2d8/cls-score-blog-improved.gif&quot; alt=&quot;Images with dimensions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;With the new advancement if CSS, you can use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio&quot;&gt;aspect-ratio&lt;/a&gt; to specify the aspect ratio of an element instead of the fixed width and height. This is useful for responsive images and videos.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16 / 9&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/db7ed289c6393a9aa520896c843ac13f/5f433/aspect-ratio.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAQX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB5lEeKwUJ/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAECAxEh/9oACAEBAAEFAuFcIuu3kzWjdP/EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ASf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAbEAABBAMAAAAAAAAAAAAAAAAAARESIQIQMf/aAAgBAQAGPwIzWLspyOqUs//EABsQAAMAAwEBAAAAAAAAAAAAAAABETFBUSFh/9oACAEBAAE/IZA4KQKvRgLFgr6bwrxle2bf0//aAAwDAQACAAMAAAAQAM//xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EGjo9P/EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGf/xAAdEAEBAAICAwEAAAAAAAAAAAABEQAhMUFRYYGR/9oACAEBAAE/ELG70YzJEBZi9PnCIExC3rn7mqV+5Yg/IQuKvfSrn//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Images with aspect-ratio&quot;
        title=&quot;Images with aspect-ratio&quot;
        src=&quot;/static/db7ed289c6393a9aa520896c843ac13f/6a068/aspect-ratio.jpg&quot;
        srcset=&quot;/static/db7ed289c6393a9aa520896c843ac13f/09b79/aspect-ratio.jpg 240w,
/static/db7ed289c6393a9aa520896c843ac13f/7cc5e/aspect-ratio.jpg 480w,
/static/db7ed289c6393a9aa520896c843ac13f/6a068/aspect-ratio.jpg 960w,
/static/db7ed289c6393a9aa520896c843ac13f/644c5/aspect-ratio.jpg 1440w,
/static/db7ed289c6393a9aa520896c843ac13f/5f433/aspect-ratio.jpg 1738w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;ads-embeds-and-iframes-without-dimensions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ads-embeds-and-iframes-without-dimensions&quot; aria-label=&quot;ads embeds and iframes without dimensions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ads, embeds, and iframes without dimensions&lt;/h3&gt;
&lt;p&gt;Ads, embeds, and iframes without specified dimensions can cause significant layout shifts. For example, on the New York Times website, content shifts when ads load without dimensions, leading to a poor user experience.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/ff6b9482c25f9175ea15b3ecf5ba4a43/cls-ads.gif&quot; alt=&quot;Ads without dimensions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Another example is when you try to click a link, but an ad loads and shifts the content, causing you to click the ad instead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/b0021bd68f12530d41a295e2f8870a39/cls-ads.gif&quot; alt=&quot;Ads without dimensions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;custom-fonts-with-side-adjust&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-fonts-with-side-adjust&quot; aria-label=&quot;custom fonts with side adjust permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom fonts with side-adjust&lt;/h3&gt;
&lt;p&gt;Custom fonts can also cause layout shifts when they load. The following video shows layout shifts caused by font loading.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/ddb92265340a5e17f800a8834d2521e2/cls-font.gif&quot; alt=&quot;Custom fonts with side-adjust&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;To mitigate this, use &lt;code class=&quot;language-text&quot;&gt;side-adjust&lt;/code&gt; to match the fallback font to the custom font. Visit &lt;a href=&quot;https://www.industrialempathy.com/perfect-ish-font-fallback/&quot;&gt;Automatic font matching for minimal CLS tool&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/68ed73479034a5d1a50776b0b86a1a4d/75b1d/cls-font-fallback.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3SSwf//EABgQAQADAQAAAAAAAAAAAAAAAAABEBFB/9oACAEBAAEFAm1Dr//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAIDAQEAAAAAAAAAAAAAAAERABBhMUH/2gAIAQEAAT8hBydpGPDT3X//2gAMAwEAAgADAAAAEFPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAQACAwEAAAAAAAAAAAAAAQARcTFBwdH/2gAIAQEAAT8QIMCzqQchGfwu27yi/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Custom fonts with side-adjust&quot;
        title=&quot;Custom fonts with side-adjust&quot;
        src=&quot;/static/68ed73479034a5d1a50776b0b86a1a4d/6a068/cls-font-fallback.jpg&quot;
        srcset=&quot;/static/68ed73479034a5d1a50776b0b86a1a4d/09b79/cls-font-fallback.jpg 240w,
/static/68ed73479034a5d1a50776b0b86a1a4d/7cc5e/cls-font-fallback.jpg 480w,
/static/68ed73479034a5d1a50776b0b86a1a4d/6a068/cls-font-fallback.jpg 960w,
/static/68ed73479034a5d1a50776b0b86a1a4d/644c5/cls-font-fallback.jpg 1440w,
/static/68ed73479034a5d1a50776b0b86a1a4d/0f98f/cls-font-fallback.jpg 1920w,
/static/68ed73479034a5d1a50776b0b86a1a4d/75b1d/cls-font-fallback.jpg 5086w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;remember-layout-shift-is-measured-cumulatively&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#remember-layout-shift-is-measured-cumulatively&quot; aria-label=&quot;remember layout shift is measured cumulatively permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Remember, Layout Shift is measured cumulatively&lt;/h3&gt;
&lt;p&gt;Layout Shift is measured as the sum of all shifts (horizontal and vertical) throughout the page’s lifespan. It is a continuous measurement.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.industrialempathy.com/posts/high-performance-web-font-loading/&quot;&gt;More than you ever wanted to know about font loading on the web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/courses/web-perf&quot;&gt;Web Performance Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Enhancing Cross-Document Navigation with the View Transitions API]]></title><link>https://trungvose.comview-transition-api-cross-document-navigation/</link><guid isPermaLink="false">https://trungvose.comview-transition-api-cross-document-navigation/</guid><pubDate>Sat, 27 Apr 2024 13:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Demo: &lt;a href=&quot;https://view-transitions.trungvose.com/&quot;&gt;https://view-transitions.trungvose.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Github: &lt;a href=&quot;https://github.com/trungvose/view-transitions-api&quot;&gt;https://github.com/trungvose/view-transitions-api&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;understanding-the-view-transitions-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#understanding-the-view-transitions-api&quot; aria-label=&quot;understanding the view transitions api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Understanding the View Transitions API&lt;/h2&gt;
&lt;p&gt;The View Transitions API helps developers create smooth animations when moving between different parts of a webpage. You’ll often notice this when going from one page to another (like from &lt;code class=&quot;language-text&quot;&gt;/page-1&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/page-2&lt;/code&gt;), but it can also make updates within the same page more dynamic.&lt;/p&gt;
&lt;p&gt;Historically, achieving seamless animations during significant state changes has posed challenges, with full-page load transitions largely dependent on browser capabilities.&lt;/p&gt;
&lt;p&gt;Last year, during Web Directions Code, I heard John Allsopp mention the View Transitions API, and its potential for simplifying this process immediately caught my attention. By leveraging CSS and a dash of JavaScript, it facilitates effortless transitioning of elements or entire pages across navigation. Initially aimed at single-page apps (SPAs), the API has evolved into a &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions-1/&quot;&gt;W3C Candidate Recommendation&lt;/a&gt; and now supports multi-page apps (MPAs) with the &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions-2/&quot;&gt;draft for cross-document navigation&lt;/a&gt; currently in public working draft status.&lt;/p&gt;
&lt;p&gt;What does this mean for developers? It signifies the ability to achieve native-like animation experiences directly within the browser. Presently, the View Transitions API can be applied in the following scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Transitioning between pages in traditional multi-page apps, including those built with server-side rendering frameworks like ASP.NET MVC, PHP, Ruby, or static site generators such as Jekyll or Hugo.&lt;/li&gt;
&lt;li&gt;Enhancing transitions within single-page applications (SPAs) crafted with frameworks like Angular, React, or Vue.&lt;/li&gt;
&lt;li&gt;Dynamically transitioning DOM changes within any web page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This guide will look into into multi-page apps initially, followed by documentation for single-page apps in subsequent parts.&lt;/p&gt;
&lt;h2 id=&quot;traditional-cross-document-navigation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#traditional-cross-document-navigation&quot; aria-label=&quot;traditional cross document navigation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Traditional Cross-Document Navigation&lt;/h2&gt;
&lt;p&gt;Conventionally, when navigating from one page to another (&lt;code class=&quot;language-text&quot;&gt;domain.com/page-1&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;domain.com/page-2&lt;/code&gt;), the browser loads a fresh HTML document for the new page, replacing the existing one. Essentially, the previous HTML is discarded, and the new one is rendered.&lt;/p&gt;
&lt;p&gt;The following code snippets, adapted from &lt;a href=&quot;https://simple-set-demos.glitch.me/1-cross-fade/&quot;&gt;this source&lt;/a&gt; with minor adjustments, illustrate this process.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- page-1.html --&gt;&lt;/span&gt;
&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://glitch.com/favicon.ico&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Cross fade demo&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;main-header&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;View Transitions Demo&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Page 1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This is the content for page 1.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Why not check out &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;./page-2.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;page 2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;?&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;circle small&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- page-2.html --&gt;&lt;/span&gt;
&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://glitch.com/favicon.ico&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Cross fade demo&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;main-header&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;./page-1.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;back-and-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;back-icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;viewBox&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0 0 24 24&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;path&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;M20 11H7.8l5.6-5.6L12 4l-8 8 8 8 1.4-1.4L7.8 13H20v-2z&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;View Transitions Demo&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Page 2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This is the content for page 2.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;It&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;also&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;has&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;list!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        Ok, that&apos;s enough fun, you can go back to
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;./page-1.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;page 1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;circle large&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 300px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 700px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://view-transitions.trungvose.com/01-mpa/page-1&quot;&gt;See Demo: MPA without View Transitions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When served via an HTTP server, clicking links between these pages results in straightforward page loads.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/f4bcbefb8e9963b24119d5fcbcfccf30/2024-04-14-view-transition-cross-document-01.gif&quot; alt=&quot;Traditional Cross-Document Navigation&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;leveraging-view-transitions-for-cross-document-navigation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#leveraging-view-transitions-for-cross-document-navigation&quot; aria-label=&quot;leveraging view transitions for cross document navigation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Leveraging View Transitions for Cross-Document Navigation&lt;/h2&gt;
&lt;p&gt;As of April 8, 2024, &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions-2/&quot;&gt;CSS View Transitions Module Level 2&lt;/a&gt; has been drafted and implemented in Chrome Canary, enabling smooth transitions between pages in multi-page apps.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/14378f8cdc89620695a624b0db86e20b/4c618/2024-04-14-view-transition-cross-document-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHtaRQxh//EABkQAAMAAwAAAAAAAAAAAAAAAAABERAhQf/aAAgBAQABBQKbgkkduP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAABAgIf/aAAgBAQAGPwIRP//EABoQAAMBAAMAAAAAAAAAAAAAAAABESFRYXH/2gAIAQEAAT8hhv6RwhbEm0ynNK7Ef//aAAwDAQACAAMAAAAQYA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAwADAQAAAAAAAAAAAAABABEhMUFxsf/aAAgBAQABPxAFoXre4DoT5EVIpbesCgWuSpp95FZP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;View Transition API: cross-document navigation&quot;
        title=&quot;View Transition API: cross-document navigation&quot;
        src=&quot;/static/14378f8cdc89620695a624b0db86e20b/6a068/2024-04-14-view-transition-cross-document-02.jpg&quot;
        srcset=&quot;/static/14378f8cdc89620695a624b0db86e20b/09b79/2024-04-14-view-transition-cross-document-02.jpg 240w,
/static/14378f8cdc89620695a624b0db86e20b/7cc5e/2024-04-14-view-transition-cross-document-02.jpg 480w,
/static/14378f8cdc89620695a624b0db86e20b/6a068/2024-04-14-view-transition-cross-document-02.jpg 960w,
/static/14378f8cdc89620695a624b0db86e20b/644c5/2024-04-14-view-transition-cross-document-02.jpg 1440w,
/static/14378f8cdc89620695a624b0db86e20b/4c618/2024-04-14-view-transition-cross-document-02.jpg 1830w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;enabling-view-transitions-in-chrome-canary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enabling-view-transitions-in-chrome-canary&quot; aria-label=&quot;enabling view transitions in chrome canary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Enabling View Transitions in Chrome Canary&lt;/h3&gt;
&lt;p&gt;To begin utilizing View Transitions, enable the feature in Chrome Canary by navigating to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//flags/#view-transition-on-navigation&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Select &lt;code class=&quot;language-text&quot;&gt;Enabled&lt;/code&gt; and restart the browser.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38dfb5fe79559e91288596ee4dea3e97/7e26b/2024-04-14-view-transition-cross-document-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 74.16666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABzZuAD//EABcQAAMBAAAAAAAAAAAAAAAAAAABERD/2gAIAQEAAQUC2uFZ/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAACAx/9oACAEBAAY/Air/AP/EABwQAQACAQUAAAAAAAAAAAAAAAEAITEQEUFhkf/aAAgBAQABPyFKy6N+48h9mLijbP/aAAwDAQACAAMAAAAQSC//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAAMBAQEAAAAAAAAAAAABEQAhMVGB0f/aAAgBAQABPxBdph2j+ZJxvxx8pxkLnmK6LTdcurJrhn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Enabling View Transitions in Chrome Canary&quot;
        title=&quot;Enabling View Transitions in Chrome Canary&quot;
        src=&quot;/static/38dfb5fe79559e91288596ee4dea3e97/6a068/2024-04-14-view-transition-cross-document-03.jpg&quot;
        srcset=&quot;/static/38dfb5fe79559e91288596ee4dea3e97/09b79/2024-04-14-view-transition-cross-document-03.jpg 240w,
/static/38dfb5fe79559e91288596ee4dea3e97/7cc5e/2024-04-14-view-transition-cross-document-03.jpg 480w,
/static/38dfb5fe79559e91288596ee4dea3e97/6a068/2024-04-14-view-transition-cross-document-03.jpg 960w,
/static/38dfb5fe79559e91288596ee4dea3e97/644c5/2024-04-14-view-transition-cross-document-03.jpg 1440w,
/static/38dfb5fe79559e91288596ee4dea3e97/7e26b/2024-04-14-view-transition-cross-document-03.jpg 1510w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;incorporating-view-transition-directive&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#incorporating-view-transition-directive&quot; aria-label=&quot;incorporating view transition directive permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Incorporating &lt;code class=&quot;language-text&quot;&gt;@view-transition&lt;/code&gt; Directive&lt;/h3&gt;
&lt;p&gt;As per the &lt;a href=&quot;https://drafts.csswg.org/css-view-transitions-2/&quot;&gt;draft&lt;/a&gt;, the &lt;code class=&quot;language-text&quot;&gt;@view-transition&lt;/code&gt; rule is pivotal for triggering view transitions during navigation. It should be included in both source and destination documents. Here’s an example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* In both documents */&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@view-transition&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;navigation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this directive in place, page transitions, such as crossfades, become seamlessly integrated across the site.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/4181094c458511486b7d21bc8e4261d8/2024-04-14-view-transition-cross-document-04.gif&quot; alt=&quot;Cross-Document Navigation with View Transitions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://view-transitions.trungvose.com/02-mpa-fade/page-1&quot;&gt;See Demo: MPA with View Transitions: Default fade-in animation&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;understanding--ua-view-transition-fade-in&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#understanding--ua-view-transition-fade-in&quot; aria-label=&quot;understanding  ua view transition fade in permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Understanding &lt;code class=&quot;language-text&quot;&gt;-ua-view-transition-fade-in&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;When troubleshooting with DevTools, you might observe the &lt;code class=&quot;language-text&quot;&gt;-ua-view-transition-fade-in&lt;/code&gt; keyframe animation applied to the &lt;code class=&quot;language-text&quot;&gt;::view-transition-new&lt;/code&gt; pseudo-element.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/56af67c06d75cf3a134e1ef5a7485a35/a8677/2024-04-14-view-transition-cross-document-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABy6EB/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGhAAAQUBAAAAAAAAAAAAAAAAAQAQETFBUf/aAAgBAQABPyG9REdb/9oADAMBAAIAAwAAABDwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQACAwEAAAAAAAAAAAAAAAEAkREhMYH/2gAIAQEAAT8QXLauFw2nrc//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Transitioning Multiple Elements&quot;
        title=&quot;Transitioning Multiple Elements&quot;
        src=&quot;/static/56af67c06d75cf3a134e1ef5a7485a35/6a068/2024-04-14-view-transition-cross-document-05.jpg&quot;
        srcset=&quot;/static/56af67c06d75cf3a134e1ef5a7485a35/09b79/2024-04-14-view-transition-cross-document-05.jpg 240w,
/static/56af67c06d75cf3a134e1ef5a7485a35/7cc5e/2024-04-14-view-transition-cross-document-05.jpg 480w,
/static/56af67c06d75cf3a134e1ef5a7485a35/6a068/2024-04-14-view-transition-cross-document-05.jpg 960w,
/static/56af67c06d75cf3a134e1ef5a7485a35/644c5/2024-04-14-view-transition-cross-document-05.jpg 1440w,
/static/56af67c06d75cf3a134e1ef5a7485a35/0f98f/2024-04-14-view-transition-cross-document-05.jpg 1920w,
/static/56af67c06d75cf3a134e1ef5a7485a35/a8677/2024-04-14-view-transition-cross-document-05.jpg 2910w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;::view-transition-new&lt;/code&gt; CSS pseudo-element represents the “new” view state of a view transition — a real-time representation of the new view after the transition. Hence, you’ll notice the fade-in effect during page navigation, transitioning from &lt;code class=&quot;language-text&quot;&gt;opacity: 0&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;opacity: 1&lt;/code&gt;, as defined in the default styling included in the UA stylesheet:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; -ua-view-transition-fade-in&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;html::view-transition-new(*)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;inset-block-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;inline-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;block-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;animation-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -ua-view-transition-fade-in&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-fill-mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more information, refer to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/::view-transition-new&quot;&gt;::view-transition-new&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;customizing-transitions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#customizing-transitions&quot; aria-label=&quot;customizing transitions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Customizing Transitions&lt;/h3&gt;
&lt;p&gt;While default fade-in/out animations are provided by the View Transitions API, developers can tailor transitions to suit their needs. For instance, creating a slide-in effect can be achieved by defining custom keyframes and applying them to the &lt;code class=&quot;language-text&quot;&gt;::view-transition-new&lt;/code&gt; pseudo-element.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slide-in&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;100px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;html::view-transition-new(root)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; slide-in&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This customization injects personality into transitions, enhancing the user experience.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/7606c297b31636cb214a00b458ca2305/2024-04-14-view-transition-cross-document-05.gif&quot; alt=&quot;Customized View Transitions&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://view-transitions.trungvose.com/03-mpa-slide-in/page-1&quot;&gt;See Demo: MPA with View Transitions: Custom animation&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;transitioning-multiple-elements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transitioning-multiple-elements&quot; aria-label=&quot;transitioning multiple elements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Transitioning Multiple Elements&lt;/h3&gt;
&lt;p&gt;While previous demonstrations focused on whole-page transitions, View Transitions also support transitioning specific elements. This is achieved by assigning a unique &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; to the element.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- In page-1.html --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;circle small&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;view-transition-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; circle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- In page-2.html --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;circle large&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;view-transition-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; circle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Even when the elements are not identical across pages, the View Transitions API intelligently manages transitions based on shared &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; attributes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/b00d76c5322784158c690e790a583004/2024-04-14-view-transition-cross-document-06.gif&quot; alt=&quot;Transitioning Multiple Elements&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://view-transitions.trungvose.com/04-mpa-circle/page-1&quot;&gt;See Demo: MPA with View Transitions: Transitioning multiple elements&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;unique-limitations-for-lists&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#unique-limitations-for-lists&quot; aria-label=&quot;unique limitations for lists permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Unique Limitations for Lists&lt;/h3&gt;
&lt;p&gt;It’s crucial to remember that each &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; must be unique. If you try to transition elements with the same name simultaneously, the transitions may fail. To ensure smooth transitions across all elements, it’s essential to manage transition names carefully.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/26d21e9d2855b4b869b5ead32021628e/07995/2024-04-14-view-transition-cross-document-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHbqWUIT//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAAIBBQAAAAAAAAAAAAAAAAABIRFRgZHw/9oACAEBAAE/IdE3Kjb5kmD/2gAMAwEAAgADAAAAELj/AP/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EFf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPxCI/8QAHRABAAICAgMAAAAAAAAAAAAAAQARIXEx4VGR8P/aAAgBAQABPxBpXPKGmFDLbqWXB81DDvqC+Huf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Transitioning Multiple Elements&quot;
        title=&quot;Transitioning Multiple Elements&quot;
        src=&quot;/static/26d21e9d2855b4b869b5ead32021628e/6a068/2024-04-14-view-transition-cross-document-04.jpg&quot;
        srcset=&quot;/static/26d21e9d2855b4b869b5ead32021628e/09b79/2024-04-14-view-transition-cross-document-04.jpg 240w,
/static/26d21e9d2855b4b869b5ead32021628e/7cc5e/2024-04-14-view-transition-cross-document-04.jpg 480w,
/static/26d21e9d2855b4b869b5ead32021628e/6a068/2024-04-14-view-transition-cross-document-04.jpg 960w,
/static/26d21e9d2855b4b869b5ead32021628e/644c5/2024-04-14-view-transition-cross-document-04.jpg 1440w,
/static/26d21e9d2855b4b869b5ead32021628e/07995/2024-04-14-view-transition-cross-document-04.jpg 1822w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Consider these examples of two cats without view transitions applied:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/402bf9f52e1e8a052a22aa0314c1a2c7/2024-04-14-view-transition-cross-document-07.gif&quot; alt=&quot;Transition Limitations&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;When I applied the &lt;code class=&quot;language-text&quot;&gt;@view-transition&lt;/code&gt; rule, you can notice the fade-in effect as seen before:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/392c9bd3dedc73631cada344e15d53bf/2024-04-14-view-transition-cross-document-08.gif&quot; alt=&quot;Transition Limitations&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;However, when I applied &lt;code class=&quot;language-text&quot;&gt;view-transition-name: cat&lt;/code&gt; to both cats, the transition didn’t work, and the fade-in animation was lost. So, I assigned each cat a different &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e07dec03ca9b1c34f59735cfa3cb4c79/5f298/2024-04-14-view-transition-cross-document-10.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB5KtxkWH/xAAXEAADAQAAAAAAAAAAAAAAAAAAAREQ/9oACAEBAAEFAtREQ//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAIDAQEAAAAAAAAAAAAAAAABEVFhMaH/2gAIAQEAAT8h6iHfpGipmRCj/9oADAMBAAIAAwAAABCb7//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EKr/xAAcEAEAAgEFAAAAAAAAAAAAAAABAFGRESExQWH/2gAIAQEAAT8QB5DX1iHTCKpmAbgxoRpT/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Transition Limitations&quot;
        title=&quot;Transition Limitations&quot;
        src=&quot;/static/e07dec03ca9b1c34f59735cfa3cb4c79/6a068/2024-04-14-view-transition-cross-document-10.jpg&quot;
        srcset=&quot;/static/e07dec03ca9b1c34f59735cfa3cb4c79/09b79/2024-04-14-view-transition-cross-document-10.jpg 240w,
/static/e07dec03ca9b1c34f59735cfa3cb4c79/7cc5e/2024-04-14-view-transition-cross-document-10.jpg 480w,
/static/e07dec03ca9b1c34f59735cfa3cb4c79/6a068/2024-04-14-view-transition-cross-document-10.jpg 960w,
/static/e07dec03ca9b1c34f59735cfa3cb4c79/644c5/2024-04-14-view-transition-cross-document-10.jpg 1440w,
/static/e07dec03ca9b1c34f59735cfa3cb4c79/0f98f/2024-04-14-view-transition-cross-document-10.jpg 1920w,
/static/e07dec03ca9b1c34f59735cfa3cb4c79/5f298/2024-04-14-view-transition-cross-document-10.jpg 2742w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now you can see the cat image smoothly transitioning between the listing page and the detail page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/b293757fef870c763e933c4fc4be21e8/2024-04-14-view-transition-cross-document-09.gif&quot; alt=&quot;Transition Limitations&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://view-transitions.trungvose.com/06-mpa-cats-view-transition-name/&quot;&gt;See Demo: MPA with View Transitions: Cats&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This means if you’re animating from a list-view to a details-view and back, ensure each element has a &lt;strong&gt;unique&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;view-transition-name&lt;/code&gt; in your template. For example, in a list of cats:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cats&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  {cats.map(cat =&gt; (
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{cat.id}&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;viewTransitionName:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;`cat-${cat.id}`&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{cat.url}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      {cat.title}
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  ))}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Assigning a unique name like &lt;code class=&quot;language-text&quot;&gt;cat-kimi&lt;/code&gt; in your rendered HTML. Then, use the same “slug” in your cat template to transition the link into the article header:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;viewTransitionName:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;`cat-${cat.id}`&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{cat.title}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  {/* Additional content goes here */}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;understanding-the-mechanism&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#understanding-the-mechanism&quot; aria-label=&quot;understanding the mechanism permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Understanding the Mechanism&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://daverupert.com/2023/05/getting-started-view-transitions/&quot;&gt;Dave’s explanation&lt;/a&gt; provides valuable insight into how the View Transitions API operates. Essentially, the browser captures snapshots of DOM elements before and after the transition, then smoothly interpolates between these states to create seamless animations.&lt;/p&gt;
&lt;p&gt;This seemingly simple mechanism is the foundation of the sophisticated transitions enabled by the View Transitions API.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.daverupert.com/posts/2023/animorphs.jpg&quot; alt=&quot;animorphs&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With View Transitions, developers can effortlessly enhance user experiences by seamlessly transitioning between pages and elements within multi-page apps. By following simple directives and carefully managing transition names, the API empowers developers to create engaging, dynamic web experiences without the need for complex JavaScript solutions.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/web-platform/view-transitions/&quot;&gt;Smooth and simple transitions with the View Transitions API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://daverupert.com/2023/05/getting-started-view-transitions/&quot;&gt;Getting started with View Transitions on multi-page apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/astro-view-transitions&quot;&gt;Astro View Transitions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Upgrading from Angular 15 to 17 in Nx Workspace: A Comprehensive Guide]]></title><link>https://trungvose.comnx-angular-17-migration/</link><guid isPermaLink="false">https://trungvose.comnx-angular-17-migration/</guid><pubDate>Fri, 29 Mar 2024 07:30:00 GMT</pubDate><content:encoded>&lt;p&gt;To explore the exciting new features of Angular 17, I am migrating Angular Spotify to the latest version. One of the notable additions in Angular 17 is the View Transition API, which is now supported by Angular Router.&lt;/p&gt;
&lt;p&gt;The View Transition API in Angular Router allows for seamless component activation and deactivation using the &lt;code class=&quot;language-text&quot;&gt;document.startViewTransition&lt;/code&gt; callback. This feature ensures smooth transitions when navigating between pages. Importantly, it gracefully handles browsers that do not support view transitions, providing a consistent navigation experience.&lt;/p&gt;
&lt;h2 id=&quot;my-current-dependencies-angular1528-and-nx1592&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#my-current-dependencies-angular1528-and-nx1592&quot; aria-label=&quot;my current dependencies angular1528 and nx1592 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;My current dependencies &lt;a href=&quot;mailto:angular@15.2.8&quot;&gt;angular@15.2.8&lt;/a&gt; and &lt;a href=&quot;mailto:nx@15.9.2&quot;&gt;nx@15.9.2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That’s how the current Angular Spotify dependencies looks like with &lt;code class=&quot;language-text&quot;&gt;angular@15.2.8&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;nx@15.9.2&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;angular-spotify&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/animations&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/common&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/compiler&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/core&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/forms&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/platform-browser&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/platform-browser-dynamic&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/router&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@angular/service-worker&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.2.8&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;devDependencies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/cli&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.9.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/cypress&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.9.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/eslint-plugin-nx&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.9.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/jest&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.9.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/js&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.9.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/linter&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.9.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/nx-cloud&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;16.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;@nrwl/workspace&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;15.9.2&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To ensure compatibility between Nx and Angular versions, it is recommended to use the latest version of Nx. Therefore, I will proceed with the command &lt;code class=&quot;language-text&quot;&gt;nx migrate latest&lt;/code&gt; to initiate the migration process.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f8512d0b86ef8af9d1141985e4e959ee/2ce39/nx-angular-17-migration-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.16666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHHRqQH/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEQETFR/9oACAEBAAE/IbZex0Uf/9oADAMBAAIAAwAAABA33//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAwEBAAAAAAAAAAAAAAEAESExkVFh/9oACAEBAAE/EN2dBB0hZb85LfGvIlEa5FprHJ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/f8512d0b86ef8af9d1141985e4e959ee/6a068/nx-angular-17-migration-01.jpg&quot;
        srcset=&quot;/static/f8512d0b86ef8af9d1141985e4e959ee/09b79/nx-angular-17-migration-01.jpg 240w,
/static/f8512d0b86ef8af9d1141985e4e959ee/7cc5e/nx-angular-17-migration-01.jpg 480w,
/static/f8512d0b86ef8af9d1141985e4e959ee/6a068/nx-angular-17-migration-01.jpg 960w,
/static/f8512d0b86ef8af9d1141985e4e959ee/644c5/nx-angular-17-migration-01.jpg 1440w,
/static/f8512d0b86ef8af9d1141985e4e959ee/2ce39/nx-angular-17-migration-01.jpg 1446w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As a reminder, the migration involves two steps:&lt;/p&gt;
&lt;h3 id=&quot;step-1-nx-migrate-latest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-nx-migrate-latest&quot; aria-label=&quot;step 1 nx migrate latest permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1. nx migrate latest&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; yarn nx migrate latest                                                                                                     &lt;span class=&quot;token number&quot;&gt;127&lt;/span&gt; ✘  &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;34&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt; 
yarn run v1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;22.19&lt;/span&gt;
$ nx migrate latest
Fetching meta data about packages&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
It may take a few minutes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
Fetching nx@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;js@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;eslint&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;plugin&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;nx@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;js@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cypress@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;eslint&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;plugin&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;nx@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;linter@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;jest@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;workspace@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;angular@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cypress@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;workspace@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;jest@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;linter@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @nrwl&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;angular@&lt;span class=&quot;token number&quot;&gt;18.1&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.2&lt;/span&gt;
Fetching @angular&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;core@&lt;span class=&quot;token number&quot;&gt;17.2&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.4&lt;/span&gt;
Fetching @ngrx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;store@&lt;span class=&quot;token number&quot;&gt;17.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;
Fetching @ngrx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;effects@&lt;span class=&quot;token number&quot;&gt;17.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;
Fetching @ngrx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;component&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;store@&lt;span class=&quot;token number&quot;&gt;17.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;
Fetching @ngrx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;store&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;devtools@&lt;span class=&quot;token number&quot;&gt;17.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;
Fetching @ngrx&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;component@&lt;span class=&quot;token number&quot;&gt;17.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;

 &lt;span class=&quot;token constant&quot;&gt;NX&lt;/span&gt;   The migrate command has run successfully&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json has been updated&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; migrations&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json has been generated&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

 &lt;span class=&quot;token constant&quot;&gt;NX&lt;/span&gt;   Next steps&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Make sure &lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json changes make sense and then run &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Run &lt;span class=&quot;token string&quot;&gt;&apos;yarn nx migrate --run-migrations&apos;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; To learn more go to https&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;nx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dev&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;features&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;automate&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;updating&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dependencies

✨  Done &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;73&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;63s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, we need to upgrade to &lt;code class=&quot;language-text&quot;&gt;angular@17.2.4&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;nx@18.1.2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cde2b418efe27c1b78dc80397e51e628/6dfd5/nx-angular-17-migration-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcukQR//xAAXEAEBAQEAAAAAAAAAAAAAAAABABEx/9oACAEBAAEFAuqWX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/AWf/xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAWEAEBAQAAAAAAAAAAAAAAAAARAAH/2gAIAQEAAT8hQs2F/9oADAMBAAIAAwAAABB4P//EABYRAAMAAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPxBOFP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QH//EABoQAQADAAMAAAAAAAAAAAAAAAEAESFRcYH/2gAIAQEAAT8QKlA0gcUezl3pn//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/cde2b418efe27c1b78dc80397e51e628/6a068/nx-angular-17-migration-02.jpg&quot;
        srcset=&quot;/static/cde2b418efe27c1b78dc80397e51e628/09b79/nx-angular-17-migration-02.jpg 240w,
/static/cde2b418efe27c1b78dc80397e51e628/7cc5e/nx-angular-17-migration-02.jpg 480w,
/static/cde2b418efe27c1b78dc80397e51e628/6a068/nx-angular-17-migration-02.jpg 960w,
/static/cde2b418efe27c1b78dc80397e51e628/644c5/nx-angular-17-migration-02.jpg 1440w,
/static/cde2b418efe27c1b78dc80397e51e628/0f98f/nx-angular-17-migration-02.jpg 1920w,
/static/cde2b418efe27c1b78dc80397e51e628/6dfd5/nx-angular-17-migration-02.jpg 3138w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7f5dead25a27f415c6ea4549d6285f6f/54d9d/nx-angular-17-migration-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHKZLRIBf/EABgQAAMBAQAAAAAAAAAAAAAAAAABERAx/9oACAEBAAEFAhwguvP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFn/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAERMVH/2gAIAQEAAT8htaFojpgKM//aAAwDAQACAAMAAAAQj9//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxAn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxAf/8QAGxABAAICAwAAAAAAAAAAAAAAAQAxESFBkaH/2gAIAQEAAT8Q1iBs3BXIJw7nrIWKLgM0T//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/7f5dead25a27f415c6ea4549d6285f6f/6a068/nx-angular-17-migration-03.jpg&quot;
        srcset=&quot;/static/7f5dead25a27f415c6ea4549d6285f6f/09b79/nx-angular-17-migration-03.jpg 240w,
/static/7f5dead25a27f415c6ea4549d6285f6f/7cc5e/nx-angular-17-migration-03.jpg 480w,
/static/7f5dead25a27f415c6ea4549d6285f6f/6a068/nx-angular-17-migration-03.jpg 960w,
/static/7f5dead25a27f415c6ea4549d6285f6f/644c5/nx-angular-17-migration-03.jpg 1440w,
/static/7f5dead25a27f415c6ea4549d6285f6f/0f98f/nx-angular-17-migration-03.jpg 1920w,
/static/7f5dead25a27f415c6ea4549d6285f6f/54d9d/nx-angular-17-migration-03.jpg 3134w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Although the changes in &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; look promising, they don’t actually perform the migration yet. The next step is where the real work begins.&lt;/p&gt;
&lt;h3 id=&quot;step-2-nx-migrate---run-migrations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-nx-migrate---run-migrations&quot; aria-label=&quot;step 2 nx migrate   run migrations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2. nx migrate —run-migrations&lt;/h3&gt;
&lt;p&gt;As the title suggest, I immediate run the next command so that it will actually do the migration&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;yarn nx migrate &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;run&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;migrations&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/d8d69ce904d79c60c9748128a552c75a/nx-angular-17-migration-04.gif&quot; alt=&quot;Migrate from Angular 15 to 17 in Nx&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;After successfully running the migration command, I encountered an error when trying to serve the application again. As expected, jumping three major versions from &lt;code class=&quot;language-text&quot;&gt;nx@15.9.2&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;nx@18.1.2&lt;/code&gt; doesn’t work smoothly.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b3f5b7fb064f51db28c07819672b5cc5/2f65b/nx-angular-17-migration-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeXFDAf/xAAWEAEBAQAAAAAAAAAAAAAAAAAhACD/2gAIAQEAAQUCZz//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAACAwEAAAAAAAAAAAAAAAAAARAhMUH/2gAIAQEAAT8hxo0zsvss/9oADAMBAAIAAwAAABBwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAQADAAIDAAAAAAAAAAAAAAEAESExUWGBkf/aAAgBAQABPxBrYFV0SwIWcQRmGEVz1FfHyPZ//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/b3f5b7fb064f51db28c07819672b5cc5/6a068/nx-angular-17-migration-05.jpg&quot;
        srcset=&quot;/static/b3f5b7fb064f51db28c07819672b5cc5/09b79/nx-angular-17-migration-05.jpg 240w,
/static/b3f5b7fb064f51db28c07819672b5cc5/7cc5e/nx-angular-17-migration-05.jpg 480w,
/static/b3f5b7fb064f51db28c07819672b5cc5/6a068/nx-angular-17-migration-05.jpg 960w,
/static/b3f5b7fb064f51db28c07819672b5cc5/644c5/nx-angular-17-migration-05.jpg 1440w,
/static/b3f5b7fb064f51db28c07819672b5cc5/0f98f/nx-angular-17-migration-05.jpg 1920w,
/static/b3f5b7fb064f51db28c07819672b5cc5/2f65b/nx-angular-17-migration-05.jpg 2296w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;proper-migration-strategy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#proper-migration-strategy&quot; aria-label=&quot;proper migration strategy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Proper migration strategy&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;To ensure a smooth upgrade process, it is recommended to follow a gradual rolling out approach when upgrading from Nx 15 to 18. Similar to the process described in &lt;a href=&quot;/blog/nx-angular-15-migration&quot;&gt;Upgrading from Angular 12 to 15 in an Nx Workspace: A Comprehensive Guide&lt;/a&gt;, we should upgrade from Nx 15 to 16, then from 16 to 17, and finally from 17 to 18.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/96a63d3f9ec8381929d62f67e9d86635/69ede/nx-angular-17-migration-13.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHHRqQH/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBAAAgMAAAAAAAAAAAAAAAAAABABQVH/2gAIAQEAAT8hKWkL/9oADAMBAAIAAwAAABCXH//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EKr/xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPxCH/8QAHBAAAwABBQAAAAAAAAAAAAAAAAERMRBRYXHR/9oACAEBAAE/ELK+dhZ+DK52MXp//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/96a63d3f9ec8381929d62f67e9d86635/6a068/nx-angular-17-migration-13.jpg&quot;
        srcset=&quot;/static/96a63d3f9ec8381929d62f67e9d86635/09b79/nx-angular-17-migration-13.jpg 240w,
/static/96a63d3f9ec8381929d62f67e9d86635/7cc5e/nx-angular-17-migration-13.jpg 480w,
/static/96a63d3f9ec8381929d62f67e9d86635/6a068/nx-angular-17-migration-13.jpg 960w,
/static/96a63d3f9ec8381929d62f67e9d86635/644c5/nx-angular-17-migration-13.jpg 1440w,
/static/96a63d3f9ec8381929d62f67e9d86635/69ede/nx-angular-17-migration-13.jpg 1864w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;nx-migrate-1670-support-angular-1620&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nx-migrate-1670-support-angular-1620&quot; aria-label=&quot;nx migrate 1670 support angular 1620 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;nx migrate 16.7.0 (support Angular 16.2.0)&lt;/h3&gt;
&lt;p&gt;Considering that I am currently using &lt;code class=&quot;language-text&quot;&gt;angular@15.2.8&lt;/code&gt;, I believe it is safe to upgrade directly to &lt;code class=&quot;language-text&quot;&gt;angular@16.2.0&lt;/code&gt;.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;See the full log of yarn nx migrate 16.7.0&lt;/summary&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate &lt;span class=&quot;token number&quot;&gt;16.7&lt;/span&gt;.0  ✔ &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;:55:12
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
$ nx migrate &lt;span class=&quot;token number&quot;&gt;16.7&lt;/span&gt;.0
Fetching meta data about packages.
It may take a few minutes.
Fetching nx@16.7.0
Fetching @nrwl/eslint-plugin-nx@16.7.0
Fetching @nrwl/linter@16.7.0
Fetching @nrwl/cypress@16.7.0
Fetching @nrwl/js@16.7.0
Fetching @nrwl/angular@16.7.0
Fetching @nrwl/jest@16.7.0
Fetching @nrwl/workspace@16.7.0
Fetching @nrwl/jest@16.7.0
Fetching @nrwl/cypress@16.7.0
Fetching @nrwl/eslint-plugin-nx@16.7.0
Fetching @nrwl/angular@16.7.0
Fetching @nrwl/workspace@16.7.0
Fetching @nrwl/linter@16.7.0
Fetching @nrwl/js@16.7.0
Fetching @angular/core@16.2.12
Fetching @ngrx/store@16.0.1
Fetching @ngrx/component-store@16.0.1
Fetching @ngrx/store-devtools@16.0.1
Fetching @ngrx/component@16.0.1
Fetching @ngrx/effects@16.0.1

 NX   The migrate &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; has run successfully.

- package.json has been updated.
- migrations.json has been generated.

 NX   Next steps:

- Make sure package.json changes &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sense and &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; run &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt;,
- Run &lt;span class=&quot;token string&quot;&gt;&apos;yarn nx migrate --run-migrations&apos;&lt;/span&gt;
- To learn &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; go to https://nx.dev/features/automate-updating-dependencies

✨  Done &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;.02s.

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations  ✔ &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;:57:26
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
$ nx migrate --run-migrations

 NX   Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍  Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning @angular-devkit/build-angular &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; guess-parser &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; @wessberg/ts-evaluator@0.0.27: this package has been renamed to ts-evaluator. Please &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ts-evaluator instead
warning @angular-devkit/build-angular &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; guess-parser &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; @wessberg/ts-evaluator &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; jsdom &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; domexception@2.0.1: Use your platform&lt;span class=&quot;token string&quot;&gt;&apos;s native DOMException instead
warning @angular-devkit/build-angular &gt; guess-parser &gt; @wessberg/ts-evaluator &gt; jsdom &gt; w3c-hr-time@1.0.2: Use your platform&apos;&lt;/span&gt;s native performance.now&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; and performance.timeOrigin.
warning @angular-devkit/build-angular &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; guess-parser &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; @wessberg/ts-evaluator &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; jsdom &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; data-urls &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; abab@2.0.6: Use your platform&lt;span class=&quot;token string&quot;&gt;&apos;s native atob() and btoa() methods instead
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning &quot;@nrwl/angular &gt; @nx/angular@16.7.0&quot; has unmet peer dependency &quot;esbuild@^0.17.5&quot;.
warning &quot; &gt; @sentry/angular@7.49.0&quot; has incorrect peer dependency &quot;@angular/common@&gt;= 10.x &amp;lt;= 15.x&quot;.
warning &quot; &gt; @sentry/angular@7.49.0&quot; has incorrect peer dependency &quot;@angular/core@&gt;= 10.x &amp;lt;= 15.x&quot;.
warning &quot; &gt; @sentry/angular@7.49.0&quot; has incorrect peer dependency &quot;@angular/router@&gt;= 10.x &amp;lt;= 15.x&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/animations@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/common@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/forms@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/core@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/platform-browser@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/router@^15.0.1&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot; has incorrect peer dependency &quot;@angular/common@^15.0.0&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot; has incorrect peer dependency &quot;@angular/core@^15.0.0&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot; has incorrect peer dependency &quot;@angular/platform-browser@^15.0.0&quot;.
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
$ node ./decorate-angular-cli.js &amp;amp;&amp;amp; ngcc --properties es2020 browser module main &amp;amp;&amp;amp; husky install

 &gt;  NX   Decoration of the Angular CLI is deprecated and will be removed in a future version

   Please replace usage of &quot;ng &amp;lt;command&gt;&quot; in any scripts, particularly for CI, with &quot;nx &amp;lt;command&gt;&quot;

 &gt;  NX   Angular CLI has been decorated to enable computation caching.

==========================================

ALERT: As of Angular 16, &quot;ngcc&quot; is no longer required and not invoked during CLI builds. You are seeing this message because the current operation invoked the &quot;ngcc&quot; command directly. This &quot;ngcc&quot; invocation can be safely removed.

A common reason for this is invoking &quot;ngcc&quot; from a &quot;postinstall&quot; hook in package.json.

In Angular 17, this command will be removed. Remove this and any other invocations to prevent errors in later versions.

==========================================

husky - Git hooks installed
$ nx migrate --run-migrations
$ nx _migrate --run-migrations

 &gt;  NX   Running migrations from &apos;&lt;/span&gt;migrations.json&lt;span class=&quot;token string&quot;&gt;&apos;

Ran 16.0.0-remove-nrwl-cli from nx
  Remove @nrwl/cli.

  UPDATE package.json
---------------------------------------------------------
Ran 16.0.0-update-nx-cloud-runner from nx
  Replace @nrwl/nx-cloud with nx-cloud

  UPDATE package.json
  UPDATE nx.json
---------------------------------------------------------
Ran 16.6.0-prefix-outputs from nx
  Prefix outputs with {workspaceRoot}/{projectRoot} if needed

  UPDATE apps/angular-spotify/project.json
  UPDATE libs/web/playlist/feature/list/project.json
  UPDATE libs/web/playlist/data-access/project.json
  UPDATE libs/web/playlist/feature/detail/project.json
  UPDATE libs/web/visualizer/feature/project.json
  UPDATE libs/web/visualizer/data-access/project.json
  UPDATE libs/web/shared/data-access/models/project.json
  UPDATE libs/web/shared/data-access/spotify-api/project.json
  UPDATE libs/web/shared/data-access/store/project.json
  UPDATE libs/web/search/feature/project.json
  UPDATE libs/web/search/data-access/project.json
  UPDATE libs/web/artist/ui/artist-top-tracks/project.json
  UPDATE libs/web/artist/ui/artist-top-track/project.json
  UPDATE libs/web/browse/data-access/project.json
  UPDATE libs/web/shared/ui/track-current-info/project.json
  UPDATE libs/web/shared/ui/icon/project.json
  UPDATE libs/web/shared/directives/click-stop-propagation/project.json
  UPDATE libs/web/shared/ui/media/project.json
  UPDATE libs/web/shared/ui/playlist-list/project.json
  UPDATE libs/web/shared/ui/spinner/project.json
  UPDATE libs/web/shared/ui/play-button/project.json
  UPDATE libs/web/shared/ui/tracks-loading/project.json
  UPDATE libs/web/shared/ui/media-order/project.json
  UPDATE libs/web/shared/ui/track-main-info/project.json
  UPDATE libs/web/shared/ui/input/project.json
  UPDATE libs/web/shared/ui/media-summary/project.json
  UPDATE libs/web/shared/ui/media-cover/project.json
  UPDATE libs/web/shared/ui/work-in-progress/project.json
  UPDATE libs/web/shared/ui/media-table/project.json
  UPDATE libs/web/shared/utils/project.json
  UPDATE libs/web/shared/app-config/project.json
  UPDATE libs/web/shared/assets/project.json
  UPDATE libs/web/artist/feature/project.json
  UPDATE libs/web/artist/data-access/project.json
  UPDATE libs/web/shared/pipes/duration-pipe/project.json
  UPDATE libs/web/visualizer/ui/project.json
  UPDATE libs/web/album/ui/album-track/project.json
  UPDATE libs/web/album/feature/detail/project.json
  UPDATE libs/web/album/feature/shell/project.json
  UPDATE libs/web/album/feature/list/project.json
  UPDATE libs/web/browse/feature/categories/project.json
  UPDATE libs/web/browse/feature/shell/project.json
  UPDATE libs/web/browse/feature/category/project.json
  UPDATE libs/web/browse/ui/category-cover/project.json
  UPDATE libs/web/album/data-access/project.json
  UPDATE libs/web/playlist/ui/playlist-track/project.json
  UPDATE libs/web/shell/ui/album-art-overlay/project.json
  UPDATE libs/web/shell/ui/player-controls/project.json
  UPDATE libs/web/shell/ui/main-view/project.json
  UPDATE libs/web/shell/ui/player-playback/project.json
  UPDATE libs/web/shell/ui/player-volume/project.json
  UPDATE libs/web/settings/data-access/project.json
  UPDATE libs/web/shell/ui/now-playing-bar/project.json
  UPDATE libs/web/shell/ui/nav-bar/project.json
  UPDATE libs/web/shell/ui/nav-links/project.json
  UPDATE libs/web/shell/ui/top-bar/project.json
  UPDATE libs/web/shell/ui/visualization-toggle/project.json
  UPDATE libs/web/home/ui/featured-playlists/project.json
  UPDATE libs/web/home/ui/greeting/project.json
  UPDATE libs/web/home/ui/recent-played/project.json
  UPDATE libs/web/home/data-access/project.json
  UPDATE libs/web/shell/ui/layout/project.json
  UPDATE libs/web/tracks/data-access/project.json
  UPDATE libs/web/shell/ui/social-share/project.json
  UPDATE libs/web/shell/ui/user-dropdown/project.json
  UPDATE libs/web/shell/feature/project.json
  UPDATE libs/web/tracks/feature/project.json
  UPDATE libs/web/auth/util/project.json
  UPDATE libs/web/home/feature/project.json
  UPDATE libs/web/auth/ui/unauthorized-modal/project.json
  UPDATE libs/web/settings/feature/project.json
  UPDATE libs/web/auth/data-access/project.json
---------------------------------------------------------
Ran update-16-0-0-add-nx-packages from @nrwl/workspace
  Replace @nrwl/workspace with @nx/workspace

  UPDATE package.json
  UPDATE .nx/cache/file-map.json
  UPDATE .nx/cache/parsed-lock-file.json
  UPDATE .nx/cache/project-graph.json
  UPDATE decorate-angular-cli.js
  UPDATE migrations.json
---------------------------------------------------------
Ran 16-0-0-move-workspace-generators-into-local-plugin from @nrwl/workspace
  Generates a plugin called &apos;&lt;/span&gt;workspace-plugin&lt;span class=&quot;token string&quot;&gt;&apos; containing your workspace generators.

  DELETE tools/generators
---------------------------------------------------------
Ran 16-0-0-fix-invalid-babelrc from @nrwl/workspace
  Fix .babelrc presets if it contains an invalid entry for @nx/web/babel.

  UPDATE libs/web/browse/data-access/.babelrc
  UPDATE libs/web/album/data-access/.babelrc
  UPDATE libs/web/tracks/data-access/.babelrc
  UPDATE libs/web/search/data-access/.babelrc
  UPDATE libs/web/settings/data-access/.babelrc
  UPDATE libs/web/home/data-access/.babelrc
---------------------------------------------------------
Ran update-16-0-0-add-nx-packages from @nrwl/eslint-plugin-nx
  Replace @nrwl/eslint-plugin-nx with @nx/eslint-plugin

  UPDATE package.json
  UPDATE .nx/cache/file-map.json
  UPDATE .nx/cache/parsed-lock-file.json
  UPDATE .nx/cache/project-graph.json
  UPDATE migrations.json
  UPDATE .eslintrc.json
  UPDATE apps/angular-spotify/.eslintrc.json
  UPDATE libs/web/album/feature/detail/.eslintrc.json
  UPDATE libs/web/album/feature/list/.eslintrc.json
  UPDATE libs/web/album/feature/shell/.eslintrc.json
  UPDATE libs/web/album/ui/album-track/.eslintrc.json
  UPDATE libs/web/artist/data-access/.eslintrc.json
  UPDATE libs/web/artist/feature/.eslintrc.json
  UPDATE libs/web/artist/ui/artist-top-track/.eslintrc.json
  UPDATE libs/web/artist/ui/artist-top-tracks/.eslintrc.json
  UPDATE libs/web/auth/ui/unauthorized-modal/.eslintrc.json
  UPDATE libs/web/browse/feature/categories/.eslintrc.json
  UPDATE libs/web/browse/feature/category/.eslintrc.json
  UPDATE libs/web/browse/feature/shell/.eslintrc.json
  UPDATE libs/web/browse/ui/category-cover/.eslintrc.json
  UPDATE libs/web/container-queries/.eslintrc.json
  UPDATE libs/web/future-responsive/.eslintrc.json
  UPDATE libs/web/home/feature/.eslintrc.json
  UPDATE libs/web/home/ui/featured-playlists/.eslintrc.json
  UPDATE libs/web/home/ui/greeting/.eslintrc.json
  UPDATE libs/web/home/ui/recent-played/.eslintrc.json
  UPDATE libs/web/playlist/feature/detail/.eslintrc.json
  UPDATE libs/web/playlist/feature/list/.eslintrc.json
  UPDATE libs/web/playlist/ui/playlist-track/.eslintrc.json
  UPDATE libs/web/search/feature/.eslintrc.json
  UPDATE libs/web/settings/feature/.eslintrc.json
  UPDATE libs/web/shared/app-init/.eslintrc.json
  UPDATE libs/web/shared/directives/click-stop-propagation/.eslintrc.json
  UPDATE libs/web/shared/directives/data-size-observer/.eslintrc.json
  UPDATE libs/web/shared/pipes/duration-pipe/.eslintrc.json
  UPDATE libs/web/shared/ui/icon/.eslintrc.json
  UPDATE libs/web/shared/ui/input/.eslintrc.json
  UPDATE libs/web/shared/ui/media/.eslintrc.json
  UPDATE libs/web/shared/ui/media-cover/.eslintrc.json
  UPDATE libs/web/shared/ui/media-order/.eslintrc.json
  UPDATE libs/web/shared/ui/media-summary/.eslintrc.json
  UPDATE libs/web/shared/ui/media-table/.eslintrc.json
  UPDATE libs/web/shared/ui/play-button/.eslintrc.json
  UPDATE libs/web/shared/ui/playlist-list/.eslintrc.json
  UPDATE libs/web/shared/ui/spinner/.eslintrc.json
  UPDATE libs/web/shared/ui/track-current-info/.eslintrc.json
  UPDATE libs/web/shared/ui/track-main-info/.eslintrc.json
  UPDATE libs/web/shared/ui/tracks-loading/.eslintrc.json
  UPDATE libs/web/shared/ui/work-in-progress/.eslintrc.json
  UPDATE libs/web/shell/feature/.eslintrc.json
  UPDATE libs/web/shell/ui/album-art-overlay/.eslintrc.json
  UPDATE libs/web/shell/ui/layout/.eslintrc.json
  UPDATE libs/web/shell/ui/main-view/.eslintrc.json
  UPDATE libs/web/shell/ui/nav-bar/.eslintrc.json
  UPDATE libs/web/shell/ui/nav-links/.eslintrc.json
  UPDATE libs/web/shell/ui/now-playing-bar/.eslintrc.json
  UPDATE libs/web/shell/ui/player-controls/.eslintrc.json
  UPDATE libs/web/shell/ui/player-playback/.eslintrc.json
  UPDATE libs/web/shell/ui/player-volume/.eslintrc.json
  UPDATE libs/web/shell/ui/social-share/.eslintrc.json
  UPDATE libs/web/shell/ui/top-bar/.eslintrc.json
  UPDATE libs/web/shell/ui/user-dropdown/.eslintrc.json
  UPDATE libs/web/shell/ui/visualization-toggle/.eslintrc.json
  UPDATE libs/web/tracks/feature/.eslintrc.json
  UPDATE libs/web/visualizer/feature/.eslintrc.json
  UPDATE libs/web/visualizer/ui/.eslintrc.json
---------------------------------------------------------
Ran update-16-0-0-add-nx-packages from @nrwl/cypress
  Replace @nrwl/cypress with @nx/cypress

  UPDATE package.json
  UPDATE .nx/cache/file-map.json
  UPDATE .nx/cache/parsed-lock-file.json
  UPDATE .nx/cache/project-graph.json
  UPDATE migrations.json
---------------------------------------------------------
Ran update-16-0-0-add-nx-packages from @nrwl/linter
  Replace @nrwl/linter with @nx/linter

  UPDATE package.json
  UPDATE apps/angular-spotify/project.json
  UPDATE libs/web/shared/data-access/spotify-api/project.json
  UPDATE libs/web/shared/data-access/store/project.json
  UPDATE libs/web/shared/data-access/models/project.json
  UPDATE libs/web/search/data-access/project.json
  UPDATE libs/web/browse/data-access/project.json
  UPDATE libs/web/album/data-access/project.json
  UPDATE libs/web/artist/data-access/project.json
  UPDATE libs/web/visualizer/data-access/project.json
  UPDATE libs/web/shared/pipes/duration-pipe/project.json
  UPDATE libs/web/shared/utils/project.json
  UPDATE libs/web/shared/app-init/project.json
  UPDATE libs/web/shared/app-config/project.json
  UPDATE libs/web/container-queries/project.json
  UPDATE libs/web/shared/ui/track-current-info/project.json
  UPDATE libs/web/shared/directives/click-stop-propagation/project.json
  UPDATE libs/web/shared/ui/icon/project.json
  UPDATE libs/web/shared/ui/media/project.json
  UPDATE libs/web/browse/feature/category/project.json
  UPDATE libs/web/browse/feature/shell/project.json
  UPDATE libs/web/browse/feature/categories/project.json
  UPDATE libs/web/shared/directives/data-size-observer/project.json
  UPDATE libs/web/search/feature/project.json
  UPDATE libs/web/shared/ui/playlist-list/project.json
  UPDATE libs/web/shared/ui/play-button/project.json
  UPDATE libs/web/shared/ui/track-main-info/project.json
  UPDATE libs/web/shared/ui/spinner/project.json
  UPDATE libs/web/shared/ui/media-order/project.json
  UPDATE libs/web/shared/ui/tracks-loading/project.json
  UPDATE libs/web/browse/ui/category-cover/project.json
  UPDATE libs/web/shared/ui/media-cover/project.json
  UPDATE libs/web/shared/ui/media-table/project.json
  UPDATE libs/web/shared/ui/input/project.json
  UPDATE libs/web/shared/ui/media-summary/project.json
  UPDATE libs/web/shared/ui/work-in-progress/project.json
  UPDATE libs/web/future-responsive/project.json
  UPDATE libs/web/artist/ui/artist-top-track/project.json
  UPDATE libs/web/visualizer/feature/project.json
  UPDATE libs/web/visualizer/ui/project.json
  UPDATE libs/web/album/feature/shell/project.json
  UPDATE libs/web/album/ui/album-track/project.json
  UPDATE libs/web/album/feature/list/project.json
  UPDATE libs/web/album/feature/detail/project.json
  UPDATE libs/web/artist/feature/project.json
  UPDATE libs/web/playlist/data-access/project.json
  UPDATE libs/web/playlist/feature/list/project.json
  UPDATE libs/web/settings/data-access/project.json
  UPDATE libs/web/playlist/feature/detail/project.json
  UPDATE libs/web/shell/ui/top-bar/project.json
  UPDATE libs/web/shell/ui/album-art-overlay/project.json
  UPDATE libs/web/shell/ui/player-controls/project.json
  UPDATE libs/web/shell/ui/player-playback/project.json
  UPDATE libs/web/auth/ui/unauthorized-modal/project.json
  UPDATE libs/web/shell/ui/layout/project.json
  UPDATE libs/web/shell/ui/now-playing-bar/project.json
  UPDATE libs/web/shell/ui/main-view/project.json
  UPDATE libs/web/shell/ui/nav-bar/project.json
  UPDATE libs/web/shell/ui/visualization-toggle/project.json
  UPDATE libs/web/shell/ui/nav-links/project.json
  UPDATE libs/web/shell/ui/social-share/project.json
  UPDATE libs/web/shell/ui/player-volume/project.json
  UPDATE libs/web/shell/ui/user-dropdown/project.json
  UPDATE libs/web/auth/data-access/project.json
  UPDATE libs/web/shell/feature/project.json
  UPDATE libs/web/auth/util/project.json
  UPDATE libs/web/settings/feature/project.json
  UPDATE libs/web/artist/ui/artist-top-tracks/project.json
  UPDATE libs/web/home/ui/greeting/project.json
  UPDATE libs/web/home/ui/recent-played/project.json
  UPDATE libs/web/playlist/ui/playlist-track/project.json
  UPDATE libs/web/tracks/data-access/project.json
  UPDATE libs/web/tracks/feature/project.json
  UPDATE libs/web/home/ui/featured-playlists/project.json
  UPDATE libs/web/home/feature/project.json
  UPDATE libs/web/home/data-access/project.json
  UPDATE .nx/cache/file-map.json
  UPDATE .nx/cache/parsed-lock-file.json
  UPDATE .nx/cache/project-graph.json
  UPDATE migrations.json
---------------------------------------------------------
Ran update-16-0-0-add-nx-packages from @nrwl/js
  Replace @nrwl/js with @nx/js

  UPDATE package.json
  UPDATE .nx/cache/file-map.json
  UPDATE .nx/cache/parsed-lock-file.json
  UPDATE .nx/cache/project-graph.json
  UPDATE migrations.json
---------------------------------------------------------
Ran update-16-0-0-add-nx-packages from @nrwl/jest
  Replace @nrwl/jest with @nx/jest

  UPDATE package.json
  UPDATE apps/angular-spotify/project.json
  UPDATE libs/web/visualizer/data-access/project.json
  UPDATE libs/web/future-responsive/project.json
  UPDATE libs/web/album/data-access/project.json
  UPDATE libs/web/artist/data-access/project.json
  UPDATE libs/web/artist/feature/project.json
  UPDATE libs/web/browse/data-access/project.json
  UPDATE libs/web/album/feature/detail/project.json
  UPDATE libs/web/album/feature/shell/project.json
  UPDATE libs/web/album/feature/list/project.json
  UPDATE libs/web/browse/ui/category-cover/project.json
  UPDATE libs/web/browse/feature/shell/project.json
  UPDATE libs/web/browse/feature/category/project.json
  UPDATE libs/web/browse/feature/categories/project.json
  UPDATE libs/web/visualizer/ui/project.json
  UPDATE libs/web/visualizer/feature/project.json
  UPDATE libs/web/tracks/feature/project.json
  UPDATE libs/web/tracks/data-access/project.json
  UPDATE libs/web/search/data-access/project.json
  UPDATE libs/web/container-queries/project.json
  UPDATE libs/web/artist/ui/artist-top-tracks/project.json
  UPDATE libs/web/shared/pipes/duration-pipe/project.json
  UPDATE libs/web/shared/data-access/spotify-api/project.json
  UPDATE libs/web/shared/data-access/store/project.json
  UPDATE libs/web/shared/app-init/project.json
  UPDATE libs/web/shared/app-config/project.json
  UPDATE libs/web/artist/ui/artist-top-track/project.json
  UPDATE libs/web/shared/ui/tracks-loading/project.json
  UPDATE libs/web/shared/ui/media-order/project.json
  UPDATE libs/web/shared/ui/icon/project.json
  UPDATE libs/web/shared/ui/media-table/project.json
  UPDATE libs/web/shared/ui/spinner/project.json
  UPDATE libs/web/shared/ui/playlist-list/project.json
  UPDATE libs/web/search/feature/project.json
  UPDATE libs/web/shared/directives/data-size-observer/project.json
  UPDATE libs/web/shared/ui/work-in-progress/project.json
  UPDATE libs/web/shared/ui/input/project.json
  UPDATE libs/web/album/ui/album-track/project.json
  UPDATE libs/web/shared/utils/project.json
  UPDATE libs/web/settings/feature/project.json
  UPDATE libs/web/settings/data-access/project.json
  UPDATE libs/web/home/data-access/project.json
  UPDATE libs/web/auth/util/project.json
  UPDATE libs/web/auth/data-access/project.json
  UPDATE libs/web/auth/ui/unauthorized-modal/project.json
  UPDATE libs/web/home/ui/recent-played/project.json
  UPDATE libs/web/playlist/ui/playlist-track/project.json
  UPDATE libs/web/shell/ui/visualization-toggle/project.json
  UPDATE libs/web/shell/ui/top-bar/project.json
  UPDATE libs/web/shell/ui/nav-bar/project.json
  UPDATE libs/web/shell/ui/nav-links/project.json
  UPDATE libs/web/shell/ui/album-art-overlay/project.json
  UPDATE libs/web/shell/ui/social-share/project.json
  UPDATE libs/web/shell/ui/user-dropdown/project.json
  UPDATE libs/web/shell/ui/layout/project.json
  UPDATE libs/web/shell/ui/main-view/project.json
  UPDATE libs/web/shell/feature/project.json
  UPDATE libs/web/home/feature/project.json
  UPDATE libs/web/playlist/data-access/project.json
  UPDATE libs/web/home/ui/featured-playlists/project.json
  UPDATE libs/web/home/ui/greeting/project.json
  UPDATE .nx/cache/file-map.json
  UPDATE .nx/cache/parsed-lock-file.json
  UPDATE .nx/cache/project-graph.json
  UPDATE jest.config.ts
  UPDATE jest.preset.js
  UPDATE migrations.json
---------------------------------------------------------
Ran add-test-setup-to-inputs-ignore from @nrwl/jest
  Add test-setup.ts to ignored files in production input

  UPDATE nx.json
---------------------------------------------------------
Ran update-16-0-0-add-nx-packages from @nrwl/angular
  Replace @nrwl/angular with @nx/angular

  UPDATE package.json
  UPDATE nx.json
  UPDATE .nx/cache/file-map.json
  UPDATE .nx/cache/parsed-lock-file.json
  UPDATE .nx/cache/project-graph.json
  UPDATE migrations.json
---------------------------------------------------------
Ran remove-ngcc-invocation from @nrwl/angular
  Remove &apos;&lt;/span&gt;ngcc&lt;span class=&quot;token string&quot;&gt;&apos; invocation if exists from the &apos;&lt;/span&gt;postinstall&lt;span class=&quot;token string&quot;&gt;&apos; script in package.json.

  UPDATE package.json
---------------------------------------------------------
Ran update-angular-cli-version-16-0-0 from @nrwl/angular
  Update the @angular/cli package version to ~16.0.0.

  UPDATE package.json
---------------------------------------------------------
Ran update-angular-cli-version-16-1-0 from @nrwl/angular
  Update the @angular/cli package version to ~16.1.0.

  UPDATE package.json
---------------------------------------------------------
Ran update-angular-cli-version-16-2-0 from @nrwl/angular
  Update the @angular/cli package version to ~16.2.0.

  UPDATE package.json
---------------------------------------------------------
Ran ngrx-component-migration-16 from @ngrx/component
  As of NgRx v16, `LetModule` and `PushModule` are deprecated in favor of standalone `LetDirective` and `PushPipe`.

  UPDATE libs/web/album/ui/album-track/src/lib/album-track.module.ts
  UPDATE libs/web/artist/ui/artist-top-track/src/lib/artist-top-track.module.ts
  UPDATE libs/web/browse/feature/category/src/lib/category.module.ts
  UPDATE libs/web/playlist/feature/detail/src/lib/playlist.module.ts
  UPDATE libs/web/playlist/ui/playlist-track/src/lib/playlist-track.module.ts
  UPDATE libs/web/shared/ui/media/src/lib/card.component.ts
  UPDATE libs/web/shared/ui/media-order/src/lib/media-order.module.ts
  UPDATE libs/web/shared/ui/play-button/src/lib/play-button.module.ts
  UPDATE libs/web/shell/ui/nav-links/src/lib/nav-links.module.ts
  UPDATE libs/web/shell/ui/visualization-toggle/src/lib/visualization-toggle.module.ts
  UPDATE libs/web/visualizer/ui/src/lib/web-visualizer-ui.module.ts
---------------------------------------------------------

 &gt;  NX   Running &apos;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; to make sure necessary packages are installed

[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning &quot; &gt; @sentry/angular@7.49.0&quot; has incorrect peer dependency &quot;@angular/common@&gt;= 10.x &amp;lt;= 15.x&quot;.
warning &quot; &gt; @sentry/angular@7.49.0&quot; has incorrect peer dependency &quot;@angular/core@&gt;= 10.x &amp;lt;= 15.x&quot;.
warning &quot; &gt; @sentry/angular@7.49.0&quot; has incorrect peer dependency &quot;@angular/router@&gt;= 10.x &amp;lt;= 15.x&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/animations@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/common@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/forms@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/core@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/platform-browser@^15.0.1&quot;.
warning &quot; &gt; ng-zorro-antd@15.1.0&quot; has incorrect peer dependency &quot;@angular/router@^15.0.1&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot; has incorrect peer dependency &quot;@angular/common@^15.0.0&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot; has incorrect peer dependency &quot;@angular/core@^15.0.0&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot; has incorrect peer dependency &quot;@angular/platform-browser@^15.0.0&quot;.
warning &quot; &gt; @nx/angular@16.7.0&quot; has unmet peer dependency &quot;esbuild@^0.17.5&quot;.
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
$ node ./decorate-angular-cli.js &amp;amp;&amp;amp; husky install

 &gt;  NX   Decoration of the Angular CLI did not complete successfully

husky - Git hooks installed

 &gt;  NX   Successfully finished running migrations from &apos;&lt;/span&gt;migrations.json&apos;. This workspace is up to date&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;

✨  Done &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;117&lt;/span&gt;.18s.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;It is crucial to run the &lt;code class=&quot;language-text&quot;&gt;serve&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;build&lt;/code&gt; command after every &lt;code class=&quot;language-text&quot;&gt;nx migrate --run-migrations&lt;/code&gt; to ensure that the application is functioning correctly. While having CI/CD configured can help catch issues when pushing code, it is always recommended to manually verify the code’s functionality before pushing.&lt;/p&gt;
&lt;p&gt;I encountered an issue when running &lt;code class=&quot;language-text&quot;&gt;yarn serve&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Error: libs/web/shell/ui/visualization-toggle/src/lib/visualization-toggle.module.ts:16:5 - error NG6002: &lt;span class=&quot;token string&quot;&gt;&apos;SvgIconsModule&apos;&lt;/span&gt; does not appear to be an NgModule class.

&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;     SvgIconsModule
       ~~~~~~~~~~~~~~

  node_modules/@ngneat/svg-icon/lib/svg-icons.module.d.ts:4:22
    &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;declare&lt;/span&gt; class SvgIconsModule &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                           ~~~~~~~~~~~~~~
    This likely means that the library &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;@ngneat/svg-icon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;which&lt;/span&gt; declares SvgIconsModule is not compatible with Angular Ivy. Check &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; a newer version of the library is available, and update &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; so. Also consider checking with the library&apos;s authors to see &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; the library is expected to be compatible with Ivy.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a5abeb455db9c8cf76eed91fc5bba1c/8cd5d/nx-angular-17-migration-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAActTIxB//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAEhIEH/2gAIAQEAAT8h5RtFx//aAAwDAQACAAMAAAAQ08//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgEFAAAAAAAAAAAAAAABABEQITFRofH/2gAIAQEAAT8QKqjV7lGgplr2C8x3x//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/1a5abeb455db9c8cf76eed91fc5bba1c/6a068/nx-angular-17-migration-06.jpg&quot;
        srcset=&quot;/static/1a5abeb455db9c8cf76eed91fc5bba1c/09b79/nx-angular-17-migration-06.jpg 240w,
/static/1a5abeb455db9c8cf76eed91fc5bba1c/7cc5e/nx-angular-17-migration-06.jpg 480w,
/static/1a5abeb455db9c8cf76eed91fc5bba1c/6a068/nx-angular-17-migration-06.jpg 960w,
/static/1a5abeb455db9c8cf76eed91fc5bba1c/644c5/nx-angular-17-migration-06.jpg 1440w,
/static/1a5abeb455db9c8cf76eed91fc5bba1c/0f98f/nx-angular-17-migration-06.jpg 1920w,
/static/1a5abeb455db9c8cf76eed91fc5bba1c/8cd5d/nx-angular-17-migration-06.jpg 2260w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It turns out that the version of &lt;code class=&quot;language-text&quot;&gt;@ngneat/svg-icon&lt;/code&gt; I was using, &lt;code class=&quot;language-text&quot;&gt;3.2.0&lt;/code&gt;, is too old. I found the latest version on GitHub, which is &lt;a href=&quot;https://github.com/ngneat/svg-icon/commit/0eac803da4e69abdcb2c8cf1d2de1368b6c362f1&quot;&gt;v7.0.3&lt;/a&gt;. To avoid any potential issues, I will commit the current Nx upgrade changes before upgrading &lt;code class=&quot;language-text&quot;&gt;@ngneat/svg-icon&lt;/code&gt;. This way, I can easily rollback if anything goes wrong.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; @ngneat/svg-icon
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; @ngneat/svg-generator --save-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0cb4b9094b407f6ff6e9292fd593f463/a162d/nx-angular-17-migration-07.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB5cVnDEH/xAAVEAEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAQABBQKEP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABsQAAICAwEAAAAAAAAAAAAAAAABEVEQMYGh/9oACAEBAAE/IUlsd8xJl2S7P//aAAwDAQACAAMAAAAQG+//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAADAAIDAAAAAAAAAAAAAAAAARFhkTFBcf/aAAgBAQABPxBi6vg0n2Ey9FJo3yzI2ZGz/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/0cb4b9094b407f6ff6e9292fd593f463/6a068/nx-angular-17-migration-07.jpg&quot;
        srcset=&quot;/static/0cb4b9094b407f6ff6e9292fd593f463/09b79/nx-angular-17-migration-07.jpg 240w,
/static/0cb4b9094b407f6ff6e9292fd593f463/7cc5e/nx-angular-17-migration-07.jpg 480w,
/static/0cb4b9094b407f6ff6e9292fd593f463/6a068/nx-angular-17-migration-07.jpg 960w,
/static/0cb4b9094b407f6ff6e9292fd593f463/644c5/nx-angular-17-migration-07.jpg 1440w,
/static/0cb4b9094b407f6ff6e9292fd593f463/0f98f/nx-angular-17-migration-07.jpg 1920w,
/static/0cb4b9094b407f6ff6e9292fd593f463/a162d/nx-angular-17-migration-07.jpg 2372w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, after performing the migration, I encountered another error when running &lt;code class=&quot;language-text&quot;&gt;yarn serve&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; libs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ui&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;src&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;lib&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;component&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;html&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; error &lt;span class=&quot;token constant&quot;&gt;NG8002&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Can&lt;span class=&quot;token string&quot;&gt;&apos;t bind to &apos;&lt;/span&gt;key&lt;span class=&quot;token string&quot;&gt;&apos; since it isn&apos;&lt;/span&gt;t a known property &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;svg-icon&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1.&lt;/span&gt; If &lt;span class=&quot;token string&quot;&gt;&apos;svg-icon&apos;&lt;/span&gt; is an Angular component and it has &lt;span class=&quot;token string&quot;&gt;&apos;key&apos;&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; then verify that it is part &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;2.&lt;/span&gt; If &lt;span class=&quot;token string&quot;&gt;&apos;svg-icon&apos;&lt;/span&gt; is a Web Component then add &lt;span class=&quot;token string&quot;&gt;&apos;CUSTOM_ELEMENTS_SCHEMA&apos;&lt;/span&gt; to the &lt;span class=&quot;token string&quot;&gt;&apos;@NgModule.schemas&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; component to suppress &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;3.&lt;/span&gt; To allow any property add &lt;span class=&quot;token string&quot;&gt;&apos;NO_ERRORS_SCHEMA&apos;&lt;/span&gt; to the &lt;span class=&quot;token string&quot;&gt;&apos;@NgModule.schemas&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; component&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;icon &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&apos;times&apos;&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg-icon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
                 &lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;

  libs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ui&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;src&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;lib&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;component&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ts&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;   &lt;span class=&quot;token literal-property property&quot;&gt;templateUrl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./web-visualizer-ui.component.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;
    Error occurs &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; the template &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; component WebVisualizerUiComponent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token literal-property property&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; libs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ui&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;src&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;lib&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ts&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; error &lt;span class=&quot;token constant&quot;&gt;TS2305&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module &lt;span class=&quot;token string&quot;&gt;&apos;&quot;@ngneat/svg-icon&quot;&apos;&lt;/span&gt; has no exported member &lt;span class=&quot;token string&quot;&gt;&apos;SvgIconsModule&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; SvgIconsModule &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@ngneat/svg-icon&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;

&lt;span class=&quot;token literal-property property&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; libs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ui&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;src&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;lib&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;web&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;visualizer&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ts&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; error &lt;span class=&quot;token constant&quot;&gt;NG1010&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Value&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/786e01b3d12ab72d823cc3c888f2fb30/0376e/nx-angular-17-migration-08.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcqoQD//xAAVEAEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAQABBQKH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFhABAQEAAAAAAAAAAAAAAAAAAQAQ/9oACAEBAAE/IRi5/9oADAMBAAIAAwAAABADz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABgQAQEBAQEAAAAAAAAAAAAAAAEAESGB/9oACAEBAAE/EA4c9IG0v//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/786e01b3d12ab72d823cc3c888f2fb30/6a068/nx-angular-17-migration-08.jpg&quot;
        srcset=&quot;/static/786e01b3d12ab72d823cc3c888f2fb30/09b79/nx-angular-17-migration-08.jpg 240w,
/static/786e01b3d12ab72d823cc3c888f2fb30/7cc5e/nx-angular-17-migration-08.jpg 480w,
/static/786e01b3d12ab72d823cc3c888f2fb30/6a068/nx-angular-17-migration-08.jpg 960w,
/static/786e01b3d12ab72d823cc3c888f2fb30/644c5/nx-angular-17-migration-08.jpg 1440w,
/static/786e01b3d12ab72d823cc3c888f2fb30/0f98f/nx-angular-17-migration-08.jpg 1920w,
/static/786e01b3d12ab72d823cc3c888f2fb30/0376e/nx-angular-17-migration-08.jpg 2370w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I noticed that in Angular, components are now exported as standalone components. According to the documentation, &lt;code class=&quot;language-text&quot;&gt;SvgIconsModule&lt;/code&gt; has been replaced with &lt;code class=&quot;language-text&quot;&gt;SvgIconComponent&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;SvgIconsModule.forRoot&lt;/code&gt; is now replaced with &lt;code class=&quot;language-text&quot;&gt;provideSvgIcons&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To update my code, I performed a global search and replace, replacing &lt;code class=&quot;language-text&quot;&gt;SvgIconsModule&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;SvgIconComponent&lt;/code&gt;. However, this resulted in a few TypeScript errors related to generics. I will fix these errors, but before that, I want to commit the current changes.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dd9c3c73116d21c7c36324418e58f620/7964b/nx-angular-17-migration-09.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcuoQD//xAAWEAEBAQAAAAAAAAAAAAAAAAAhEBH/2gAIAQEAAQUCMJ//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAEDH/2gAIAQEABj8CKv/EABgQAAMBAQAAAAAAAAAAAAAAAAABIUFR/9oACAEBAAE/IVo6MJ0//9oADAMBAAIAAwAAABDwD//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EKr/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAYEAADAQEAAAAAAAAAAAAAAAAAARFhcf/aAAgBAQABPxAorWoyHQ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/dd9c3c73116d21c7c36324418e58f620/6a068/nx-angular-17-migration-09.jpg&quot;
        srcset=&quot;/static/dd9c3c73116d21c7c36324418e58f620/09b79/nx-angular-17-migration-09.jpg 240w,
/static/dd9c3c73116d21c7c36324418e58f620/7cc5e/nx-angular-17-migration-09.jpg 480w,
/static/dd9c3c73116d21c7c36324418e58f620/6a068/nx-angular-17-migration-09.jpg 960w,
/static/dd9c3c73116d21c7c36324418e58f620/644c5/nx-angular-17-migration-09.jpg 1440w,
/static/dd9c3c73116d21c7c36324418e58f620/0f98f/nx-angular-17-migration-09.jpg 1920w,
/static/dd9c3c73116d21c7c36324418e58f620/7964b/nx-angular-17-migration-09.jpg 2278w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;type-string-is-not-assignable-to-type-genericstorestatus&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#type-string-is-not-assignable-to-type-genericstorestatus&quot; aria-label=&quot;type string is not assignable to type genericstorestatus permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Type ‘string’ is not assignable to type ‘GenericStoreStatus’&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0d8d3d736d96ca132764d3ddfc9439b8/f5df4/nx-angular-17-migration-10.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 84.58333333333331%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAARABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeTnUIBAA//EABYQAAMAAAAAAAAAAAAAAAAAABAgQf/aAAgBAQABBQIxf//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABQQAQAAAAAAAAAAAAAAAAAAADD/2gAIAQEABj8CH//EABYQAQEBAAAAAAAAAAAAAAAAAEEQIP/aAAgBAQABPyEo1//aAAwDAQACAAMAAAAQYAc8/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPxAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPxAf/8QAHRAAAgEEAwAAAAAAAAAAAAAAAAEhEDFRoRFB8P/aAAgBAQABPxBuHbJyjh52PANTbR6w71//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/0d8d3d736d96ca132764d3ddfc9439b8/6a068/nx-angular-17-migration-10.jpg&quot;
        srcset=&quot;/static/0d8d3d736d96ca132764d3ddfc9439b8/09b79/nx-angular-17-migration-10.jpg 240w,
/static/0d8d3d736d96ca132764d3ddfc9439b8/7cc5e/nx-angular-17-migration-10.jpg 480w,
/static/0d8d3d736d96ca132764d3ddfc9439b8/6a068/nx-angular-17-migration-10.jpg 960w,
/static/0d8d3d736d96ca132764d3ddfc9439b8/644c5/nx-angular-17-migration-10.jpg 1440w,
/static/0d8d3d736d96ca132764d3ddfc9439b8/0f98f/nx-angular-17-migration-10.jpg 1920w,
/static/0d8d3d736d96ca132764d3ddfc9439b8/f5df4/nx-angular-17-migration-10.jpg 2286w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now TypeScript is reporting a new error. The problem is that the &lt;code class=&quot;language-text&quot;&gt;GenericStoreStatus&lt;/code&gt; type only allows four specific string values.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GenericStoreStatus&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;pending&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;success&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the reducer function, when I only return a new object with &lt;code class=&quot;language-text&quot;&gt;status: &apos;loading&apos;&lt;/code&gt;, TypeScript implicitly assumes that the type of &lt;code class=&quot;language-text&quot;&gt;status&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;string&lt;/code&gt;. To explicitly specify that &lt;code class=&quot;language-text&quot;&gt;status&lt;/code&gt; should have the type &lt;code class=&quot;language-text&quot;&gt;GenericStoreStatus&lt;/code&gt;, I need to provide a type annotation or use a type assertion.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; albumsReducer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  initialState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loadAlbums&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    error&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To resolve this issue, you can cast the status value to &lt;code class=&quot;language-text&quot;&gt;GenericStoreStatus&lt;/code&gt; using &lt;code class=&quot;language-text&quot;&gt;&apos;loading&apos; as GenericStoreStatus&lt;/code&gt;. Alternatively, you can use the &lt;code class=&quot;language-text&quot;&gt;as const&lt;/code&gt; assertion to ensure the status value is treated as a specific string literal type.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;export const albumsReducer = createReducer(
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; initialState,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; on(loadAlbums, (state) =&gt; ({
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   ...state,
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;    status: &apos;loading&apos;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    status: &apos;loading&apos; as GenericStoreStatus,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    // OR
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    status: &apos;loading&apos; as const,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   error: null
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; })),&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To fix the issue, you can use multi-cursor editing. Here’s a gif demonstrating the process:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/48051b9985c2b9585cf61c960e401e19/nx-angular-17-migration-11.gif&quot; alt=&quot;Migrate from Angular 15 to 17 in Nx&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Running &lt;code class=&quot;language-text&quot;&gt;nx serve&lt;/code&gt; again and it is working now.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/87a7f21d7e2743b79198ba7d073467e3/fe12a/nx-angular-17-migration-12.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAByVeUrAX/xAAXEAADAQAAAAAAAAAAAAAAAAAAARAC/9oACAEBAAEFAnWZn//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAIDAQEAAAAAAAAAAAAAAAABETFBYaH/2gAIAQEAAT8hbnpE0S1rKGxn/9oADAMBAAIAAwAAABD3z//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/EBn/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgEFAAAAAAAAAAAAAAABADEQEUFhcYH/2gAIAQEAAT8QWleqDKitpoAA4GVYLnU//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/87a7f21d7e2743b79198ba7d073467e3/6a068/nx-angular-17-migration-12.jpg&quot;
        srcset=&quot;/static/87a7f21d7e2743b79198ba7d073467e3/09b79/nx-angular-17-migration-12.jpg 240w,
/static/87a7f21d7e2743b79198ba7d073467e3/7cc5e/nx-angular-17-migration-12.jpg 480w,
/static/87a7f21d7e2743b79198ba7d073467e3/6a068/nx-angular-17-migration-12.jpg 960w,
/static/87a7f21d7e2743b79198ba7d073467e3/644c5/nx-angular-17-migration-12.jpg 1440w,
/static/87a7f21d7e2743b79198ba7d073467e3/0f98f/nx-angular-17-migration-12.jpg 1920w,
/static/87a7f21d7e2743b79198ba7d073467e3/fe12a/nx-angular-17-migration-12.jpg 3528w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;nx-migrate-1730-support-angular-1710&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nx-migrate-1730-support-angular-1710&quot; aria-label=&quot;nx migrate 1730 support angular 1710 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;nx migrate 17.3.0 (support Angular 17.1.0)&lt;/h3&gt;
&lt;p&gt;I ran two commands&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate &lt;span class=&quot;token number&quot;&gt;17.3&lt;/span&gt;.0
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;See the full log of nx migrate 17.3.0&lt;/summary&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate &lt;span class=&quot;token number&quot;&gt;17.3&lt;/span&gt;.0                                    ✔ &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;:20:23
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
$ nx migrate &lt;span class=&quot;token number&quot;&gt;17.3&lt;/span&gt;.0
Fetching meta data about packages.
It may take a few minutes.
Fetching nx@17.3.0
Fetching @nx/js@17.3.0
Fetching @nx/jest@17.3.0
Fetching @nx/linter@17.3.0
Fetching @nx/workspace@17.3.0
Fetching @nx/angular@17.3.0
Fetching @nx/cypress@17.3.0
Fetching @nx/eslint-plugin@17.3.0
Fetching @nx/linter@17.3.0
Fetching @angular/core@17.1.3
Fetching @ngrx/store@17.0.1
Fetching @ngrx/component-store@17.0.1
Fetching @ngrx/component@17.0.1
Fetching @ngrx/store-devtools@17.0.1
Fetching @ngrx/effects@17.0.1

 NX   The migrate &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; has run successfully.

- package.json has been updated.
- migrations.json has been generated.


 NX   Next steps:

- Make sure package.json changes &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sense and &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; run &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt;,
- Run &lt;span class=&quot;token string&quot;&gt;&apos;yarn nx migrate --run-migrations&apos;&lt;/span&gt;
- To learn &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; go to https://nx.dev/features/automate-updating-dependencies

✨  Done &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;45&lt;/span&gt;.81s.

&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations                ✔  40s   &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;:27:51 
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
$ nx migrate --run-migrations

 NX   Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍  Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🚚  Fetching packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔗  Linking dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/animations@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/forms@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngneat/svg-generator@7.0.3&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;svgo@&gt;=3.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔨  Building fresh packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
success Saved lockfile.
$ &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; ./decorate-angular-cli.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

 &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX   Decoration of the Angular CLI did not complete successfully

husky - Git hooks installed
$ nx migrate --run-migrations
$ nx _migrate --run-migrations

 &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX   Running migrations from &lt;span class=&quot;token string&quot;&gt;&apos;migrations.json&apos;&lt;/span&gt;

Ran &lt;span class=&quot;token number&quot;&gt;17.0&lt;/span&gt;.0-move-cache-directory from nx
  Updates the default cache directory to .nx/cache

  UPDATE .prettierignore
---------------------------------------------------------
Ran &lt;span class=&quot;token number&quot;&gt;17.0&lt;/span&gt;.0-use-minimal-config-for-tasks-runner-options from nx
  Use minimal config &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; tasksRunnerOptions

  UPDATE package.json
  UPDATE nx.json
---------------------------------------------------------
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;object Object&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
Ran rm-default-collection-npm-scope from nx
  Migration &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; v17.0.0-rc.1

  UPDATE nx.json
---------------------------------------------------------
Ran update-angular-cli-version-17-0-0 from @nx/angular
  Update the @angular/cli package version to ~17.0.0.

  UPDATE package.json
---------------------------------------------------------
Ran rename-browser-target-to-build-target from @nx/angular
  Rename &lt;span class=&quot;token string&quot;&gt;&apos;browserTarget&apos;&lt;/span&gt; to &lt;span class=&quot;token string&quot;&gt;&apos;buildTarget&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;

  UPDATE apps/angular-spotify/project.json
---------------------------------------------------------
Ran update-angular-cli-version-17-1-0 from @nx/angular
  Update the @angular/cli package version to ~17.1.0.

  UPDATE package.json
---------------------------------------------------------
Ran move-options-to-target-defaults from @nx/jest
  Move jest executor options to nx.json targetDefaults

  UPDATE nx.json
  UPDATE apps/angular-spotify/project.json
  UPDATE libs/web/album/data-access/project.json
  UPDATE libs/web/album/feature/detail/project.json
  UPDATE libs/web/album/feature/list/project.json
  UPDATE libs/web/album/feature/shell/project.json
  UPDATE libs/web/album/ui/album-track/project.json
  UPDATE libs/web/artist/data-access/project.json
  UPDATE libs/web/artist/feature/project.json
  UPDATE libs/web/artist/ui/artist-top-track/project.json
  UPDATE libs/web/artist/ui/artist-top-tracks/project.json
  UPDATE libs/web/auth/data-access/project.json
  UPDATE libs/web/auth/ui/unauthorized-modal/project.json
  UPDATE libs/web/auth/util/project.json
  UPDATE libs/web/browse/data-access/project.json
  UPDATE libs/web/browse/feature/categories/project.json
  UPDATE libs/web/browse/feature/category/project.json
  UPDATE libs/web/browse/feature/shell/project.json
  UPDATE libs/web/browse/ui/category-cover/project.json
  UPDATE libs/web/container-queries/project.json
  UPDATE libs/web/future-responsive/project.json
  UPDATE libs/web/home/data-access/project.json
  UPDATE libs/web/home/feature/project.json
  UPDATE libs/web/home/ui/featured-playlists/project.json
  UPDATE libs/web/home/ui/greeting/project.json
  UPDATE libs/web/home/ui/recent-played/project.json
  UPDATE libs/web/playlist/data-access/project.json
  UPDATE libs/web/playlist/ui/playlist-track/project.json
  UPDATE libs/web/search/data-access/project.json
  UPDATE libs/web/search/feature/project.json
  UPDATE libs/web/settings/data-access/project.json
  UPDATE libs/web/settings/feature/project.json
  UPDATE libs/web/shared/app-config/project.json
  UPDATE libs/web/shared/app-init/project.json
  UPDATE libs/web/shared/data-access/spotify-api/project.json
  UPDATE libs/web/shared/data-access/store/project.json
  UPDATE libs/web/shared/directives/data-size-observer/project.json
  UPDATE libs/web/shared/pipes/duration-pipe/project.json
  UPDATE libs/web/shared/ui/icon/project.json
  UPDATE libs/web/shared/ui/input/project.json
  UPDATE libs/web/shared/ui/media-order/project.json
  UPDATE libs/web/shared/ui/media-table/project.json
  UPDATE libs/web/shared/ui/playlist-list/project.json
  UPDATE libs/web/shared/ui/spinner/project.json
  UPDATE libs/web/shared/ui/tracks-loading/project.json
  UPDATE libs/web/shared/ui/work-in-progress/project.json
  UPDATE libs/web/shared/utils/project.json
  UPDATE libs/web/shell/feature/project.json
  UPDATE libs/web/shell/ui/album-art-overlay/project.json
  UPDATE libs/web/shell/ui/layout/project.json
  UPDATE libs/web/shell/ui/main-view/project.json
  UPDATE libs/web/shell/ui/nav-bar/project.json
  UPDATE libs/web/shell/ui/nav-links/project.json
  UPDATE libs/web/shell/ui/social-share/project.json
  UPDATE libs/web/shell/ui/top-bar/project.json
  UPDATE libs/web/shell/ui/user-dropdown/project.json
  UPDATE libs/web/shell/ui/visualization-toggle/project.json
  UPDATE libs/web/tracks/data-access/project.json
  UPDATE libs/web/tracks/feature/project.json
  UPDATE libs/web/visualizer/data-access/project.json
  UPDATE libs/web/visualizer/feature/project.json
  UPDATE libs/web/visualizer/ui/project.json
---------------------------------------------------------
Ran update-17-0-0-rename-to-eslint from @nx/linter
  update-17-0-0-rename-to-eslint

  UPDATE package.json
  UPDATE apps/angular-spotify/project.json
  UPDATE libs/web/album/data-access/project.json
  UPDATE libs/web/album/feature/detail/project.json
  UPDATE libs/web/album/feature/list/project.json
  UPDATE libs/web/album/feature/shell/project.json
  UPDATE libs/web/album/ui/album-track/project.json
  UPDATE libs/web/artist/data-access/project.json
  UPDATE libs/web/artist/feature/project.json
  UPDATE libs/web/artist/ui/artist-top-track/project.json
  UPDATE libs/web/artist/ui/artist-top-tracks/project.json
  UPDATE libs/web/auth/data-access/project.json
  UPDATE libs/web/auth/ui/unauthorized-modal/project.json
  UPDATE libs/web/auth/util/project.json
  UPDATE libs/web/browse/data-access/project.json
  UPDATE libs/web/browse/feature/categories/project.json
  UPDATE libs/web/browse/feature/category/project.json
  UPDATE libs/web/browse/feature/shell/project.json
  UPDATE libs/web/browse/ui/category-cover/project.json
  UPDATE libs/web/container-queries/project.json
  UPDATE libs/web/future-responsive/project.json
  UPDATE libs/web/home/data-access/project.json
  UPDATE libs/web/home/feature/project.json
  UPDATE libs/web/home/ui/featured-playlists/project.json
  UPDATE libs/web/home/ui/greeting/project.json
  UPDATE libs/web/home/ui/recent-played/project.json
  UPDATE libs/web/playlist/data-access/project.json
  UPDATE libs/web/playlist/feature/detail/project.json
  UPDATE libs/web/playlist/feature/list/project.json
  UPDATE libs/web/playlist/ui/playlist-track/project.json
  UPDATE libs/web/search/data-access/project.json
  UPDATE libs/web/search/feature/project.json
  UPDATE libs/web/settings/data-access/project.json
  UPDATE libs/web/settings/feature/project.json
  UPDATE libs/web/shared/app-config/project.json
  UPDATE libs/web/shared/app-init/project.json
  UPDATE libs/web/shared/data-access/models/project.json
  UPDATE libs/web/shared/data-access/spotify-api/project.json
  UPDATE libs/web/shared/data-access/store/project.json
  UPDATE libs/web/shared/directives/click-stop-propagation/project.json
  UPDATE libs/web/shared/directives/data-size-observer/project.json
  UPDATE libs/web/shared/pipes/duration-pipe/project.json
  UPDATE libs/web/shared/ui/icon/project.json
  UPDATE libs/web/shared/ui/input/project.json
  UPDATE libs/web/shared/ui/media/project.json
  UPDATE libs/web/shared/ui/media-cover/project.json
  UPDATE libs/web/shared/ui/media-order/project.json
  UPDATE libs/web/shared/ui/media-summary/project.json
  UPDATE libs/web/shared/ui/media-table/project.json
  UPDATE libs/web/shared/ui/play-button/project.json
  UPDATE libs/web/shared/ui/playlist-list/project.json
  UPDATE libs/web/shared/ui/spinner/project.json
  UPDATE libs/web/shared/ui/track-current-info/project.json
  UPDATE libs/web/shared/ui/track-main-info/project.json
  UPDATE libs/web/shared/ui/tracks-loading/project.json
  UPDATE libs/web/shared/ui/work-in-progress/project.json
  UPDATE libs/web/shared/utils/project.json
  UPDATE libs/web/shell/feature/project.json
  UPDATE libs/web/shell/ui/album-art-overlay/project.json
  UPDATE libs/web/shell/ui/layout/project.json
  UPDATE libs/web/shell/ui/main-view/project.json
  UPDATE libs/web/shell/ui/nav-bar/project.json
  UPDATE libs/web/shell/ui/nav-links/project.json
  UPDATE libs/web/shell/ui/now-playing-bar/project.json
  UPDATE libs/web/shell/ui/player-controls/project.json
  UPDATE libs/web/shell/ui/player-playback/project.json
  UPDATE libs/web/shell/ui/player-volume/project.json
  UPDATE libs/web/shell/ui/social-share/project.json
  UPDATE libs/web/shell/ui/top-bar/project.json
  UPDATE libs/web/shell/ui/user-dropdown/project.json
  UPDATE libs/web/shell/ui/visualization-toggle/project.json
  UPDATE libs/web/tracks/data-access/project.json
  UPDATE libs/web/tracks/feature/project.json
  UPDATE libs/web/visualizer/data-access/project.json
  UPDATE libs/web/visualizer/feature/project.json
  UPDATE libs/web/visualizer/ui/project.json
  UPDATE migrations.json
---------------------------------------------------------
Ran simplify-eslint-patterns from @nx/linter
  Simplify eslintFilePatterns

  UPDATE apps/angular-spotify/project.json
  UPDATE libs/web/album/data-access/project.json
  UPDATE libs/web/album/feature/detail/project.json
  UPDATE libs/web/album/feature/list/project.json
  UPDATE libs/web/album/feature/shell/project.json
  UPDATE libs/web/album/ui/album-track/project.json
  UPDATE libs/web/artist/data-access/project.json
  UPDATE libs/web/artist/feature/project.json
  UPDATE libs/web/artist/ui/artist-top-track/project.json
  UPDATE libs/web/artist/ui/artist-top-tracks/project.json
  UPDATE libs/web/auth/data-access/project.json
  UPDATE libs/web/auth/ui/unauthorized-modal/project.json
  UPDATE libs/web/auth/util/project.json
  UPDATE libs/web/browse/data-access/project.json
  UPDATE libs/web/browse/feature/categories/project.json
  UPDATE libs/web/browse/feature/category/project.json
  UPDATE libs/web/browse/feature/shell/project.json
  UPDATE libs/web/browse/ui/category-cover/project.json
  UPDATE libs/web/container-queries/project.json
  UPDATE libs/web/future-responsive/project.json
  UPDATE libs/web/home/data-access/project.json
  UPDATE libs/web/home/feature/project.json
  UPDATE libs/web/home/ui/featured-playlists/project.json
  UPDATE libs/web/home/ui/greeting/project.json
  UPDATE libs/web/home/ui/recent-played/project.json
  UPDATE libs/web/playlist/data-access/project.json
  UPDATE libs/web/playlist/feature/detail/project.json
  UPDATE libs/web/playlist/feature/list/project.json
  UPDATE libs/web/playlist/ui/playlist-track/project.json
  UPDATE libs/web/search/data-access/project.json
  UPDATE libs/web/search/feature/project.json
  UPDATE libs/web/settings/data-access/project.json
  UPDATE libs/web/settings/feature/project.json
  UPDATE libs/web/shared/app-config/project.json
  UPDATE libs/web/shared/app-init/project.json
  UPDATE libs/web/shared/data-access/models/project.json
  UPDATE libs/web/shared/data-access/spotify-api/project.json
  UPDATE libs/web/shared/data-access/store/project.json
  UPDATE libs/web/shared/directives/click-stop-propagation/project.json
  UPDATE libs/web/shared/directives/data-size-observer/project.json
  UPDATE libs/web/shared/pipes/duration-pipe/project.json
  UPDATE libs/web/shared/ui/icon/project.json
  UPDATE libs/web/shared/ui/input/project.json
  UPDATE libs/web/shared/ui/media/project.json
  UPDATE libs/web/shared/ui/media-cover/project.json
  UPDATE libs/web/shared/ui/media-order/project.json
  UPDATE libs/web/shared/ui/media-summary/project.json
  UPDATE libs/web/shared/ui/media-table/project.json
  UPDATE libs/web/shared/ui/play-button/project.json
  UPDATE libs/web/shared/ui/playlist-list/project.json
  UPDATE libs/web/shared/ui/spinner/project.json
  UPDATE libs/web/shared/ui/track-current-info/project.json
  UPDATE libs/web/shared/ui/track-main-info/project.json
  UPDATE libs/web/shared/ui/tracks-loading/project.json
  UPDATE libs/web/shared/ui/work-in-progress/project.json
  UPDATE libs/web/shared/utils/project.json
  UPDATE libs/web/shell/feature/project.json
  UPDATE libs/web/shell/ui/album-art-overlay/project.json
  UPDATE libs/web/shell/ui/layout/project.json
  UPDATE libs/web/shell/ui/main-view/project.json
  UPDATE libs/web/shell/ui/nav-bar/project.json
  UPDATE libs/web/shell/ui/nav-links/project.json
  UPDATE libs/web/shell/ui/now-playing-bar/project.json
  UPDATE libs/web/shell/ui/player-controls/project.json
  UPDATE libs/web/shell/ui/player-playback/project.json
  UPDATE libs/web/shell/ui/player-volume/project.json
  UPDATE libs/web/shell/ui/social-share/project.json
  UPDATE libs/web/shell/ui/top-bar/project.json
  UPDATE libs/web/shell/ui/user-dropdown/project.json
  UPDATE libs/web/shell/ui/visualization-toggle/project.json
  UPDATE libs/web/tracks/data-access/project.json
  UPDATE libs/web/tracks/feature/project.json
  UPDATE libs/web/visualizer/data-access/project.json
  UPDATE libs/web/visualizer/feature/project.json
  UPDATE libs/web/visualizer/ui/project.json
---------------------------------------------------------
Ran move-options-to-target-defaults from @nx/linter
  Move executor options to target defaults

  UPDATE nx.json
---------------------------------------------------------
Ran block-template-entities from @angular/core
  Angular v17 introduces a new control flow syntax that uses the @ and &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; characters. This migration replaces the existing usages with their corresponding HTML entities.

  UPDATE libs/web/shared/ui/work-in-progress/src/lib/work-in-progress.component.html
---------------------------------------------------------
Ran ngrx-store-devtools-migration-17-0-0-beta from @ngrx/store-devtools
  The road to v17-beta.1

  UPDATE libs/web/shell/feature/src/lib/build-specifics/index.ts
---------------------------------------------------------

 &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX   Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍  Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🚚  Fetching packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔗  Linking dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/animations@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/forms@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngneat/svg-generator@7.0.3&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;svgo@&gt;=3.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔨  Building fresh packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
success Saved lockfile.
$ &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; ./decorate-angular-cli.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

 &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX   Decoration of the Angular CLI did not complete successfully

husky - Git hooks installed

 &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX   Successfully finished running migrations from &lt;span class=&quot;token string&quot;&gt;&apos;migrations.json&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; This workspace is up to date&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;I encountered an issue while running &lt;code class=&quot;language-text&quot;&gt;nx serve&lt;/code&gt; this time. The problem is in the &lt;code class=&quot;language-text&quot;&gt;promp-update.service.ts&lt;/code&gt; file where the &lt;code class=&quot;language-text&quot;&gt;UpdateAvailableEvent&lt;/code&gt; has been removed. To resolve this, I followed the instructions provided in the &lt;a href=&quot;https://angular.io/guide/service-worker-communications#updating-to-the-latest-version&quot;&gt;Updating to the latest version&lt;/a&gt; guide to force update the service worker.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Error: libs/web/shared/app-init/src/lib/promp-update.service.ts:3:20 - error TS2305: Module &lt;span class=&quot;token string&quot;&gt;&apos;&quot;@angular/service-worker&quot;&apos;&lt;/span&gt; has no exported member &lt;span class=&quot;token string&quot;&gt;&apos;UpdateAvailableEvent&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; SwUpdate, UpdateAvailableEvent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; from &lt;span class=&quot;token string&quot;&gt;&apos;@angular/service-worker&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                     ~~~~~~~~~~~~~~~~~~~~

Error: libs/web/shared/app-init/src/lib/promp-update.service.ts:17:25 - error TS2339: Property &lt;span class=&quot;token string&quot;&gt;&apos;available&apos;&lt;/span&gt; does not exist on &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SwUpdate&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;     &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; this.updates.available.pipe&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                           ~~~~~~~~~

Error: libs/web/shared/app-init/src/lib/promp-update.service.ts:21:55 - error TS18046: &lt;span class=&quot;token string&quot;&gt;&apos;version&apos;&lt;/span&gt; is of &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;unknown&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;             &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Angular Spotify&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; PWA is updating from $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;version.current.hash&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; to $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;version.available.hash&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
                                                         ~~~~~~~

Error: libs/web/shared/app-init/src/lib/promp-update.service.ts:21:82 - error TS18046: &lt;span class=&quot;token string&quot;&gt;&apos;version&apos;&lt;/span&gt; is of &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;unknown&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;

&lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;             &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Angular Spotify&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; PWA is updating from $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;version.current.hash&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; to $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;version.available.hash&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e3e47122f04e32d2ebd41b2c2c719f53/86a42/nx-angular-17-migration-14.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByYgQf//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAIDAAAAAAAAAAAAAAAAAAEQABFB/9oACAEBAAE/IbL2Ff/aAAwDAQACAAMAAAAQMM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAACAwEBAAAAAAAAAAAAAAAAAREhMRBR/9oACAEBAAE/EPBjrSWNKVGFz//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/e3e47122f04e32d2ebd41b2c2c719f53/6a068/nx-angular-17-migration-14.jpg&quot;
        srcset=&quot;/static/e3e47122f04e32d2ebd41b2c2c719f53/09b79/nx-angular-17-migration-14.jpg 240w,
/static/e3e47122f04e32d2ebd41b2c2c719f53/7cc5e/nx-angular-17-migration-14.jpg 480w,
/static/e3e47122f04e32d2ebd41b2c2c719f53/6a068/nx-angular-17-migration-14.jpg 960w,
/static/e3e47122f04e32d2ebd41b2c2c719f53/644c5/nx-angular-17-migration-14.jpg 1440w,
/static/e3e47122f04e32d2ebd41b2c2c719f53/0f98f/nx-angular-17-migration-14.jpg 1920w,
/static/e3e47122f04e32d2ebd41b2c2c719f53/86a42/nx-angular-17-migration-14.jpg 3560w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here is the updated code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;forceUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Observable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;VersionEvent &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;updates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEnabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;updates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;versionUpdates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;VERSION_READY&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;updates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activateUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;confirm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;There is a new version of Angular Spotify available! Would you like to upgrade now?&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running &lt;code class=&quot;language-text&quot;&gt;nx serve&lt;/code&gt; again and it is working now.&lt;/p&gt;
&lt;h3 id=&quot;nx-migrate-1820-support-angular-1730&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nx-migrate-1820-support-angular-1730&quot; aria-label=&quot;nx migrate 1820 support angular 1730 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;nx migrate 18.2.0 (support Angular 17.3.0)&lt;/h3&gt;
&lt;p&gt;This should be the easiest one as it is just a minor update.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate &lt;span class=&quot;token number&quot;&gt;18.2&lt;/span&gt;.0
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;See the full log of nx migrate 18.2.0&lt;/summary&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate &lt;span class=&quot;token number&quot;&gt;18.2&lt;/span&gt;.0                          ✔  &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;:48:04
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
$ nx migrate &lt;span class=&quot;token number&quot;&gt;18.2&lt;/span&gt;.0
Fetching meta data about packages.
It may take a few minutes.
Fetching nx@18.2.0
Fetching @nx/js@18.2.0
Fetching @nx/eslint@18.2.0
Fetching @nx/eslint-plugin@18.2.0
Fetching @nx/jest@18.2.0
Fetching @nx/workspace@18.2.0
Fetching @nx/angular@18.2.0
Fetching @nx/cypress@18.2.0
Fetching @angular/core@17.3.2

 NX   The migrate &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; has run successfully.

- package.json has been updated.
- migrations.json has been generated.


 NX   Next steps:

- Make sure package.json changes &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sense and &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; run &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt;,
- Run &lt;span class=&quot;token string&quot;&gt;&apos;yarn nx migrate --run-migrations&apos;&lt;/span&gt;
- To learn &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; go to https://nx.dev/features/automate-updating-dependencies

✨  Done &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;.74s.

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations               ✔ &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;:49:19
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
$ nx migrate --run-migrations

 NX   Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍  Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🚚  Fetching packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔗  Linking dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/animations@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/forms@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngneat/svg-generator@7.0.3&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;svgo@&gt;=3.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔨  Building fresh packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
success Saved lockfile.
$ &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; ./decorate-angular-cli.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

 NX   Decoration of the Angular CLI did not complete successfully

husky - Git hooks installed
$ nx migrate --run-migrations
$ nx _migrate --run-migrations

 NX   Running migrations from &lt;span class=&quot;token string&quot;&gt;&apos;migrations.json&apos;&lt;/span&gt;

Ran &lt;span class=&quot;token number&quot;&gt;18.0&lt;/span&gt;.0-disable-adding-plugins-for-existing-workspaces from nx
  Updates nx.json to disabled adding plugins when generating projects &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; an existing Nx workspace

  UPDATE nx.json
---------------------------------------------------------
Ran update-angular-cli-version-17-2-0 from @nx/angular
  Update the @angular/cli package version to ~17.2.0.

  UPDATE package.json
---------------------------------------------------------
Ran move-default-base-to-nx-json-root from nx
  Moves affected.defaultBase to defaultBase &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;nx.json&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt;

  UPDATE nx.json
---------------------------------------------------------
Ran update-angular-cli-version-17-3-0 from @nx/angular
  Update the @angular/cli package version to ~17.3.0.

  UPDATE package.json
---------------------------------------------------------

 NX   Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍  Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🚚  Fetching packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔗  Linking dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@7.49.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@&gt;= 10.x &amp;lt;= 15.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/animations@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/forms@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@15.1.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@^15.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@15.2.8&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0 || ^16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@15.0.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^15.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngneat/svg-generator@7.0.3&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;svgo@&gt;=3.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔨  Building fresh packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
success Saved lockfile.
$ &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; ./decorate-angular-cli.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

 NX   Decoration of the Angular CLI did not complete successfully

husky - Git hooks installed

 NX   Successfully finished running migrations from &lt;span class=&quot;token string&quot;&gt;&apos;migrations.json&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; This workspace is up to date&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;

✨  Done &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;73&lt;/span&gt;.12s.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;Voila! The migration is done. I have successfully migrated from Angular 15 to Angular 17 in Nx.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5c939336f6abf063aa7d013d53c47002/dc423/nx-angular-17-migration-15.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHmsEqD/8QAFhAAAwAAAAAAAAAAAAAAAAAAARAg/9oACAEBAAEFAoC//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEQESEx/9oACAEBAAE/IcFV4y2Pkv/aAAwDAQACAAMAAAAQDP8A/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEB/9oACAEDAQE/EImP/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEB/9oACAECAQE/EKuv/8QAGxABAAIDAQEAAAAAAAAAAAAAAQARITFRQXH/2gAIAQEAAT8Qtgt5OyxCGOx97mj5NmGp/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        title=&quot;Migrate from Angular 15 to 17 in Nx&quot;
        src=&quot;/static/5c939336f6abf063aa7d013d53c47002/6a068/nx-angular-17-migration-15.jpg&quot;
        srcset=&quot;/static/5c939336f6abf063aa7d013d53c47002/09b79/nx-angular-17-migration-15.jpg 240w,
/static/5c939336f6abf063aa7d013d53c47002/7cc5e/nx-angular-17-migration-15.jpg 480w,
/static/5c939336f6abf063aa7d013d53c47002/6a068/nx-angular-17-migration-15.jpg 960w,
/static/5c939336f6abf063aa7d013d53c47002/644c5/nx-angular-17-migration-15.jpg 1440w,
/static/5c939336f6abf063aa7d013d53c47002/0f98f/nx-angular-17-migration-15.jpg 1920w,
/static/5c939336f6abf063aa7d013d53c47002/dc423/nx-angular-17-migration-15.jpg 2689w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Improve Largest Contentful Paint (LCP)]]></title><description><![CDATA[Learn how to improve Largest Contentful Paint (LCP)]]></description><link>https://trungvose.comweb-performance-improve-lcp/</link><guid isPermaLink="false">https://trungvose.comweb-performance-improve-lcp/</guid><pubDate>Thu, 01 Feb 2024 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Times really flies. Since the last article on &lt;a href=&quot;/blog/web-performance-improve-fcp&quot;&gt;improving FCP&lt;/a&gt;, it has been almost half a year. In this article, we’ll explore how to improve Largest Contentful Paint (LCP).&lt;/p&gt;
&lt;h2 id=&quot;largest-contentful-paint-lcp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#largest-contentful-paint-lcp&quot; aria-label=&quot;largest contentful paint lcp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Largest Contentful Paint (LCP)&lt;/h2&gt;
&lt;p&gt;Largest Contentful Paint (LCP) is one of the three Core Web Vitals metrics, and it represents how quickly the main content of a web page is loaded. Specifically, LCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport.&lt;/p&gt;
&lt;h3 id=&quot;what-elements-are-considered&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-elements-are-considered&quot; aria-label=&quot;what elements are considered permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What elements are considered?&lt;/h3&gt;
&lt;p&gt;As currently specified in the &lt;a href=&quot;https://wicg.github.io/largest-contentful-paint/&quot;&gt;Largest Contentful Paint API&lt;/a&gt;, the types of elements considered for Largest Contentful Paint are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; elements (the &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/2023_08_lcp.md&quot;&gt;first frame presentation time&lt;/a&gt; is used for animated content such as GIFs or animated PNGs)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;image&gt;&lt;/code&gt; elements inside an &lt;code class=&quot;language-text&quot;&gt;&amp;lt;svg&gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;video&gt;&lt;/code&gt; elements (the poster image load time or &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/2023_08_lcp.md&quot;&gt;first frame presentation time&lt;/a&gt; for videos is used—whichever is earlier)&lt;/li&gt;
&lt;li&gt;An element with a background image loaded using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/url()&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;url()&lt;/code&gt;&lt;/a&gt; function, (as opposed to a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_Images/Using_CSS_gradients&quot;&gt;CSS gradient&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Block-level_elements&quot;&gt;Block-level&lt;/a&gt; elements containing text nodes or other inline-level text element children.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;understanding-your-lcp-metric&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#understanding-your-lcp-metric&quot; aria-label=&quot;understanding your lcp metric permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Understanding your LCP metric&lt;/h3&gt;
&lt;p&gt;Before optimizing LCP, developers should seek to understand if they even have an LCP issue, and the extent of any such issue.&lt;/p&gt;
&lt;p&gt;LCP can be measured in a number of tools and not all of these measure LCP in the same way. To understand LCP of real users, we should look at what real users are experiencing, rather than what a lab-based tool like Lighthouse or local testing shows. These lab-based tools can give a wealth of information to explain and help you improve LCP, but be aware that lab tests alone may not be entirely representative of what your actual users experience.&lt;/p&gt;
&lt;p&gt;LCP data based on real users can be surfaced from Real User Monitoring (RUM) tools installed on a site, or by using the &lt;a href=&quot;https://developer.chrome.com/docs/crux&quot;&gt;Chrome User Experience Report (CrUX)&lt;/a&gt; which collect anonymous data from real Chrome users for millions of websites&lt;/p&gt;
&lt;p&gt;The Performance panel of Chrome DevTools shows your local LCP experience next to the page or origin’s CrUX LCP in the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/performance/overview#live-metrics&quot;&gt;live metrics view&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fa0f38540aaadfa6fd25a77fd6f37797/53a88/improve-lcp-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.83333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAQAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2URiP//EABUQAQEAAAAAAAAAAAAAAAAAACBB/9oACAEBAAEFAqf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAVEAEBAAAAAAAAAAAAAAAAAAAgMf/aAAgBAQAGPwKr/8QAGRAAAwEBAQAAAAAAAAAAAAAAAAERQSGh/9oACAEBAAE/Id+YUTpemCP/2gAMAwEAAgADAAAAEADP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxAAAwEBAAMAAAAAAAAAAAAAAAERITFRoeH/2gAIAQEAAT8Q14IlfsKls4QVejDYvAs4GqP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;LCP in Chrome DevTools&quot;
        title=&quot;LCP in Chrome DevTools&quot;
        src=&quot;/static/fa0f38540aaadfa6fd25a77fd6f37797/6a068/improve-lcp-01.jpg&quot;
        srcset=&quot;/static/fa0f38540aaadfa6fd25a77fd6f37797/09b79/improve-lcp-01.jpg 240w,
/static/fa0f38540aaadfa6fd25a77fd6f37797/7cc5e/improve-lcp-01.jpg 480w,
/static/fa0f38540aaadfa6fd25a77fd6f37797/6a068/improve-lcp-01.jpg 960w,
/static/fa0f38540aaadfa6fd25a77fd6f37797/644c5/improve-lcp-01.jpg 1440w,
/static/fa0f38540aaadfa6fd25a77fd6f37797/0f98f/improve-lcp-01.jpg 1920w,
/static/fa0f38540aaadfa6fd25a77fd6f37797/53a88/improve-lcp-01.jpg 4186w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-good-lcp-score&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-a-good-lcp-score&quot; aria-label=&quot;what is a good lcp score permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is a good LCP score?&lt;/h3&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have an LCP of &lt;code class=&quot;language-text&quot;&gt;2.5&lt;/code&gt; seconds or less for at least 75% of page visits.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/18cf07e2ef89a518694ec55e430144a7/06b9a/lcp-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd2AsD//xAAYEAACAwAAAAAAAAAAAAAAAAAAAREhQf/aAAgBAQABBQLYFR//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAADAQAAAAAAAAAAAAAAAAAAARAy/9oACAEBAAY/AjTn/8QAGBABAQEBAQAAAAAAAAAAAAAAAREAMWH/2gAIAQEAAT8hnVcuuREq+u//2gAMAwEAAgADAAAAEAPP/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAh/9oACAEDAQE/EBF//8QAFxEAAwEAAAAAAAAAAAAAAAAAARAhMf/aAAgBAgEBPxA3V//EABoQAQADAAMAAAAAAAAAAAAAAAEAESExcaH/2gAIAQEAAT8QFsxfF5HIBe0JnkUVV7Gf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;LCP Threshold&quot;
        title=&quot;LCP Threshold&quot;
        src=&quot;/static/18cf07e2ef89a518694ec55e430144a7/6a068/lcp-score.jpg&quot;
        srcset=&quot;/static/18cf07e2ef89a518694ec55e430144a7/09b79/lcp-score.jpg 240w,
/static/18cf07e2ef89a518694ec55e430144a7/7cc5e/lcp-score.jpg 480w,
/static/18cf07e2ef89a518694ec55e430144a7/6a068/lcp-score.jpg 960w,
/static/18cf07e2ef89a518694ec55e430144a7/644c5/lcp-score.jpg 1440w,
/static/18cf07e2ef89a518694ec55e430144a7/06b9a/lcp-score.jpg 1874w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;improve-lcp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#improve-lcp&quot; aria-label=&quot;improve lcp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Improve LCP&lt;/h2&gt;
&lt;p&gt;Now that we know how to get the &lt;a href=&quot;/blog/web-performance-improve-fcp&quot;&gt;first bytes out the door&lt;/a&gt;, we can make some infrastructure changes to ensure that we’re delivering our bytes as quickly as possible.&lt;/p&gt;
&lt;p&gt;But how do we ensure that the bytes we’re sending to load the page are the right ones to reach this point of completion as soon as possible? As a reminder, the Largest Contentful Paint (LCP) is the time between the start of the page load and when the user sees that most of the page is ready, or almost ready.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/123296d78f426485078de85ec3603b95/12609/improve-lcp-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHVoyRYCv/EABoQAAEFAQAAAAAAAAAAAAAAABEAARASITH/2gAIAQEAAQUCbhiuDV//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPwFZ/8QAGRABAAIDAAAAAAAAAAAAAAAAAQAQAhEh/9oACAEBAAY/AhWcyrV//8QAHBABAAICAwEAAAAAAAAAAAAAAQAhETFRgaGx/9oACAEBAAE/IXqAnMxT6QgS7yUsD1Axqf/aAAwDAQACAAMAAAAQc+//xAAWEQEBAQAAAAAAAAAAAAAAAAABEFH/2gAIAQMBAT8QDJ//xAAWEQADAAAAAAAAAAAAAAAAAAABEFH/2gAIAQIBAT8QJF//xAAdEAEBAAIDAAMAAAAAAAAAAAABEQAxIUFhobHh/9oACAEBAAE/EEV/Ndqb+cFtvr8yoWXzCQ0VGO26yFpWXh9YBghn/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Improve LCP&quot;
        title=&quot;Improve LCP&quot;
        src=&quot;/static/123296d78f426485078de85ec3603b95/6a068/improve-lcp-02.jpg&quot;
        srcset=&quot;/static/123296d78f426485078de85ec3603b95/09b79/improve-lcp-02.jpg 240w,
/static/123296d78f426485078de85ec3603b95/7cc5e/improve-lcp-02.jpg 480w,
/static/123296d78f426485078de85ec3603b95/6a068/improve-lcp-02.jpg 960w,
/static/123296d78f426485078de85ec3603b95/644c5/improve-lcp-02.jpg 1440w,
/static/123296d78f426485078de85ec3603b95/0f98f/improve-lcp-02.jpg 1920w,
/static/123296d78f426485078de85ec3603b95/12609/improve-lcp-02.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let’s look at the sequence of events in a simplified version of what we would see in the performance panel of Google DevTools. At the beginning of a request, the first thing we need to do is fetch the HTML document. We need to pull down the initial document and have the browser parse it to determine what other resources are needed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As the document is being parsed, the browser finds other resources that need to be downloaded, such as a CSS file in the head of the page. This CSS file might then require additional resources like a background image, a font, or another CSS file import, causing a chain of events that can increase load time.&lt;/li&gt;
&lt;li&gt;You’ll also likely have images in the file, which the browser will try to download in parallel as much as possible. However, depending on the number of resources and the browser’s parallel download capabilities, some images might be deferred until later when there is available bandwidth.&lt;/li&gt;
&lt;li&gt;Most applications will have some JavaScript, which often pulls down additional assets, such as other JavaScript files or modules. This can create a pattern where JavaScript loads more JavaScript, further delaying the LCP.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we attach our metrics here, the First Contentful Paint (FCP) occurs right after the HTML is downloaded and the first elements are rendered on the screen. However, the LCP happens only after all other resources, such as images, JavaScript, and CSS, have been fully loaded and rendered.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So how do we achieve a faster LCP? We do this by reducing the number of tasks the browser needs to perform and focusing on delivering the user’s most important content as quickly as possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For a well-optimized page, you want your LCP resource request to start loading as early as it can, and you want the LCP element to render as quickly as possible after the LCP resource finishes loading. To help visualize whether or not a particular page is following this principle, you can break down the total LCP time into the following sub-parts:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c5716cfb837892a2562a1c4c52e62c49/d6f75/improve-lcp-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHem1DAr//EABcQAAMBAAAAAAAAAAAAAAAAAAEQEQD/2gAIAQEAAQUCOrgX/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAECAx/9oACAEBAAY/Akg//8QAGRABAAIDAAAAAAAAAAAAAAAAAQAQETFh/9oACAEBAAE/IUkypB2Tkr//2gAMAwEAAgADAAAAEAvP/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8Qh//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAECAQE/EKf/xAAbEAEAAgIDAAAAAAAAAAAAAAABABEQMSFR8f/aAAgBAQABPxBUoEYHd8wui9zSjXZPAx//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Improve LCP&quot;
        title=&quot;Improve LCP&quot;
        src=&quot;/static/c5716cfb837892a2562a1c4c52e62c49/6a068/improve-lcp-03.jpg&quot;
        srcset=&quot;/static/c5716cfb837892a2562a1c4c52e62c49/09b79/improve-lcp-03.jpg 240w,
/static/c5716cfb837892a2562a1c4c52e62c49/7cc5e/improve-lcp-03.jpg 480w,
/static/c5716cfb837892a2562a1c4c52e62c49/6a068/improve-lcp-03.jpg 960w,
/static/c5716cfb837892a2562a1c4c52e62c49/644c5/improve-lcp-03.jpg 1440w,
/static/c5716cfb837892a2562a1c4c52e62c49/0f98f/improve-lcp-03.jpg 1920w,
/static/c5716cfb837892a2562a1c4c52e62c49/d6f75/improve-lcp-03.jpg 8000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time to First Byte (TTFB)&lt;/strong&gt;: The time from when the user initiates loading the page until the browser receives the first byte of the HTML document response.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource load delay&lt;/strong&gt;: The time between TTFB and when the browser starts loading the LCP resource. If the LCP element doesn’t require a resource load to render (for example, if the element is a text node rendered with a system font), this time is 0.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource load duration&lt;/strong&gt;: The duration of time it takes to load the LCP resource itself. If the LCP element doesn’t require a resource load to render, this time is 0.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Element render delay&lt;/strong&gt;: The time between when the LCP resource finishes loading and the LCP element rendering fully.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;1-eliminate-resource-load-delay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-eliminate-resource-load-delay&quot; aria-label=&quot;1 eliminate resource load delay permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Eliminate resource load delay&lt;/h3&gt;
&lt;p&gt;The goal in this step is to ensure the LCP resource starts loading as early as possible. While in theory the earliest a resource could start loading is immediately after TTFB, in practice there is always some delay before browsers actually start loading resources.&lt;/p&gt;
&lt;p&gt;A good rule of thumb is that your LCP resource should start loading at the same time as the first resource loaded by that page. Or, to put that another way, if the LCP resource starts loading later than the first resource, then there’s opportunity for improvement.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b94e0d28d29a95f54b0fe90c7821b5b6/0f98f/improve-lcp-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7gWkf/xAAVEAEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAQABBQJr/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhAAAgIDAAAAAAAAAAAAAAAAASEAEBExYf/aAAgBAQABPyFtFx8xYr//2gAMAwEAAgADAAAAEHDP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8QZ//EABwQAAICAgMAAAAAAAAAAAAAAAEhABEQQVGBwf/aAAgBAQABPxAOABVxSr7UiYCmpvueMf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Eliminate resource load delay&quot;
        title=&quot;Eliminate resource load delay&quot;
        src=&quot;/static/b94e0d28d29a95f54b0fe90c7821b5b6/6a068/improve-lcp-04.jpg&quot;
        srcset=&quot;/static/b94e0d28d29a95f54b0fe90c7821b5b6/09b79/improve-lcp-04.jpg 240w,
/static/b94e0d28d29a95f54b0fe90c7821b5b6/7cc5e/improve-lcp-04.jpg 480w,
/static/b94e0d28d29a95f54b0fe90c7821b5b6/6a068/improve-lcp-04.jpg 960w,
/static/b94e0d28d29a95f54b0fe90c7821b5b6/644c5/improve-lcp-04.jpg 1440w,
/static/b94e0d28d29a95f54b0fe90c7821b5b6/0f98f/improve-lcp-04.jpg 1920w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Generally speaking, there are two factors that affect how quickly an LCP resource can be loading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the resource is discovered.&lt;/li&gt;
&lt;li&gt;What priority the resource is given.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Optimize when the resource is discovered&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To ensure your LCP resource starts loading as early as possible, it’s critical that the resource is discoverable in the initial HTML document response by the browser’s preload scanner. For example, in the following cases, the browser can discover the LCP resource by scanning the HTML document response:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The LCP element is an &lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; element, and its src or srcset attributes are present in the initial HTML markup.&lt;/li&gt;
&lt;li&gt;The LCP element requires a CSS background image, but that image is preloaded using &lt;code class=&quot;language-text&quot;&gt;&amp;lt;link rel=&quot;preload&quot;&gt;&lt;/code&gt; in the HTML markup (or using a Link header).&lt;/li&gt;
&lt;li&gt;The LCP element is a text node that requires a web font to render, and the font is loaded using &lt;code class=&quot;language-text&quot;&gt;&amp;lt;link rel=&quot;preload&quot;&gt;&lt;/code&gt; in the HTML markup (or using a Link header).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are some examples where the LCP resource cannot be discovered from scanning the HTML document response:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The LCP element is an &lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; that is dynamically added to the page using JavaScript.&lt;/li&gt;
&lt;li&gt;The LCP element is lazily loaded with a JavaScript library that hides its src or srcset attributes (often as &lt;code class=&quot;language-text&quot;&gt;data-src&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;data-srcset&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The LCP element requires a CSS background image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each of these cases, the browser needs to run the script or apply the stylesheet—which usually involves waiting for network requests to finish—before it can discover the LCP resource and could start loading it. This is never &lt;em&gt;optimal&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To eliminate unnecessary resource load delay, your LCP resource should be discoverable from the HTML source. In cases where the resource is only referenced from an external CSS or JavaScript file, the LCP resource should be preloaded with a &lt;strong&gt;high fetch priority&lt;/strong&gt;, for example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Load the stylesheet that will reference the LCP image. --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Preload the LCP image with a high fetchpriority so it starts loading with the stylesheet. --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/hero-image.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Optimize the priority the resource is given&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even if the LCP resource is discoverable from the HTML markup, it still may not start loading as early as the first resource. This can happen if the browser preload scanner’s priority heuristics don’t recognize that the resource is important, or if it determines that other resources are more important.&lt;/p&gt;
&lt;p&gt;For example, you can delay your LCP image using HTML if you set &lt;code class=&quot;language-text&quot;&gt;loading=&quot;lazy&quot;&lt;/code&gt; on your &lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; element. Using lazy loading means that the resource won’t be loaded until after layout confirms the image is in the viewport and so may begin loading later than it otherwise would.&lt;/p&gt;
&lt;p&gt;Even without lazy loading, images are not initially loaded with the highest priority by browsers as they are not render-blocking resources. You can hint to the browser as to which resources are most important using the fetchpriority attribute for resources that could benefit from a higher priority:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/hero-image.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After you have optimized your LCP resource priority and discovery time, your network waterfall should look like this (with the LCP resource starting at the same time as the first resource):&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8a598f638a34855e6b388ffa83f05e15/0f98f/improve-lcp-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe6wWkf/xAAVEAEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAQABBQJr/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhAAAgIDAAAAAAAAAAAAAAAAAREAECExUf/aAAgBAQABPyHLRUDHDHQr/9oADAMBAAIAAwAAABAwz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGf/xAAcEAACAgIDAAAAAAAAAAAAAAAAASExEMERQYH/2gAIAQEAAT8QRXWTh2bAoFyUwd+mmP/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Optimized network waterfall&quot;
        title=&quot;Optimized network waterfall&quot;
        src=&quot;/static/8a598f638a34855e6b388ffa83f05e15/6a068/improve-lcp-05.jpg&quot;
        srcset=&quot;/static/8a598f638a34855e6b388ffa83f05e15/09b79/improve-lcp-05.jpg 240w,
/static/8a598f638a34855e6b388ffa83f05e15/7cc5e/improve-lcp-05.jpg 480w,
/static/8a598f638a34855e6b388ffa83f05e15/6a068/improve-lcp-05.jpg 960w,
/static/8a598f638a34855e6b388ffa83f05e15/644c5/improve-lcp-05.jpg 1440w,
/static/8a598f638a34855e6b388ffa83f05e15/0f98f/improve-lcp-05.jpg 1920w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-eliminate-element-render-delay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-eliminate-element-render-delay&quot; aria-label=&quot;2 eliminate element render delay permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Eliminate element render delay&lt;/h3&gt;
&lt;p&gt;The goal in this step is to ensure the LCP element can render immediately after its resource has finished loading, no matter when that happens.&lt;/p&gt;
&lt;p&gt;The primary reason the LCP element wouldn’t be able to render immediately after its resource finishes loading is if rendering is blocked for some other reason:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rendering of the entire page is blocked due to &lt;code class=&quot;language-text&quot;&gt;stylesheets&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;synchronous scripts&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;head&gt;&lt;/code&gt; that are still loading.&lt;/li&gt;
&lt;li&gt;The LCP resource has finished loading, but the LCP element has not yet been added to the DOM (it’s waiting for some JavaScript code to load).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Defer or inline render-blocking JavaScript&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is almost never necessary to add synchronous scripts (scripts without the &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;defer&lt;/code&gt; attributes) to the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;head&gt;&lt;/code&gt; of your pages, and doing so will almost always have a negative impact on performance.&lt;/p&gt;
&lt;p&gt;In cases where JavaScript code needs to run as early as possible in the page load, it’s best to inline it so rendering isn’t delayed waiting on another network request. As with stylesheets, though, you should only inline scripts if they’re very small.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;❌ DON’T&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/main.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;✅ DO&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Inline script contents directly in the HTML.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// IMPORTANT: only do this for very small scripts.&lt;/span&gt;
  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-reduce-resource-load-duration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-reduce-resource-load-duration&quot; aria-label=&quot;3 reduce resource load duration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Reduce resource load duration&lt;/h3&gt;
&lt;p&gt;The goal of this step is to reduce the time spent transferring the bytes of the resource over the network to the user’s device. In general, there are four ways to do that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduce the size of the resource
&lt;ul&gt;
&lt;li&gt;Use modern image formats&lt;/li&gt;
&lt;li&gt;Serve the optimal image size&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reduce the distance the resource has to travel.
&lt;ul&gt;
&lt;li&gt;Image CDNs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reduce contention for network bandwidth.
&lt;ul&gt;
&lt;li&gt;Lazy load images&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Eliminate the network time entirely.
&lt;ul&gt;
&lt;li&gt;cache-control headers&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;font-display: swap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/reduce-webfont-size&quot;&gt;Reduce web font size&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Use modern image formats.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The LCP resource of a page (if it has one) will either be an image or a web font. The following guides go into great detail about how to reduce the size of both:&lt;/p&gt;
&lt;p&gt;AVIF and WebP are image formats that have superior compression and quality characteristics compared to their older JPEG and PNG counterparts. Encoding your images in these formats rather than JPEG or PNG means that they will load faster and consume less cellular data.&lt;/p&gt;
&lt;p&gt;AVIF is supported in Chrome, Firefox, and Opera and offers smaller file sizes compared to other formats with the same quality settings.&lt;/p&gt;
&lt;p&gt;WebP is supported in the latest versions of Chrome, Firefox, Safari, Edge, and Opera and provides better lossy and lossless compression for images on the web&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/313c1b26a17e448576cba0b98c175f3b/db7be/improve-lcp-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.916666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdSAuD//xAAYEAEAAwEAAAAAAAAAAAAAAAACAAEREv/aAAgBAQABBQI05j5NKf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EAB0QAAEDBQEAAAAAAAAAAAAAAAACETEDEiEyQaH/2gAIAQEABj8CpuqJNs3eCnX0/8QAGxABAAICAwAAAAAAAAAAAAAAAQAhEUFRYXH/2gAIAQEAAT8hF3XuVzmrAXAtpWp//9oADAMBAAIAAwAAABCAD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAAIDAQAAAAAAAAAAAAAAAAERACExQf/aAAgBAQABPxAEgUxF+QPQpSDrIhoAFKop/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;WebP vs AVIF vs JPEG&quot;
        title=&quot;WebP vs AVIF vs JPEG&quot;
        src=&quot;/static/313c1b26a17e448576cba0b98c175f3b/6a068/improve-lcp-06.jpg&quot;
        srcset=&quot;/static/313c1b26a17e448576cba0b98c175f3b/09b79/improve-lcp-06.jpg 240w,
/static/313c1b26a17e448576cba0b98c175f3b/7cc5e/improve-lcp-06.jpg 480w,
/static/313c1b26a17e448576cba0b98c175f3b/6a068/improve-lcp-06.jpg 960w,
/static/313c1b26a17e448576cba0b98c175f3b/644c5/improve-lcp-06.jpg 1440w,
/static/313c1b26a17e448576cba0b98c175f3b/0f98f/improve-lcp-06.jpg 1920w,
/static/313c1b26a17e448576cba0b98c175f3b/db7be/improve-lcp-06.jpg 3700w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Image CDNs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Image CDNs are a type of CDN that specializes in serving images. They can help reduce the distance the resource has to travel by caching images closer to the user and provide additional features like image optimization and resizing.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/53df49d30d52d520bcdc6e81123a3eb7/12609/img-cdn-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe8Q0g//xAAWEAEBAQAAAAAAAAAAAAAAAAAQESH/2gAIAQEAAQUC1j//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAIDH/2gAIAQEABj8CIv8A/8QAGhABAAIDAQAAAAAAAAAAAAAAAQAQETFBgf/aAAgBAQABPyFRoz7Be0RRP//aAAwDAQACAAMAAAAQEw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgIDAQAAAAAAAAAAAAABACERMUFRcWH/2gAIAQEAAT8QYpEZeg+OYOYhVL9j667ibvmf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Image CDNs&quot;
        title=&quot;Image CDNs&quot;
        src=&quot;/static/53df49d30d52d520bcdc6e81123a3eb7/6a068/img-cdn-01.jpg&quot;
        srcset=&quot;/static/53df49d30d52d520bcdc6e81123a3eb7/09b79/img-cdn-01.jpg 240w,
/static/53df49d30d52d520bcdc6e81123a3eb7/7cc5e/img-cdn-01.jpg 480w,
/static/53df49d30d52d520bcdc6e81123a3eb7/6a068/img-cdn-01.jpg 960w,
/static/53df49d30d52d520bcdc6e81123a3eb7/644c5/img-cdn-01.jpg 1440w,
/static/53df49d30d52d520bcdc6e81123a3eb7/0f98f/img-cdn-01.jpg 1920w,
/static/53df49d30d52d520bcdc6e81123a3eb7/12609/img-cdn-01.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/44fbf1af203824c13400c077cda2dae8/12609/img-cdn-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQCAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABsjqEJD4f/8QAGxAAAgIDAQAAAAAAAAAAAAAAAQMAAgQRFBP/2gAIAQEAAQUC6L+hyWbo+5hx1E86jAlYn//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABsQAQADAAMBAAAAAAAAAAAAAAEAAiERElEy/9oACAEBAAY/ArnYAfJTQ5I6O+TloT4MmUJ//8QAGRABAQEBAQEAAAAAAAAAAAAAAREAITFh/9oACAEBAAE/IU2vAXOi7lZ93jAQJiZNermKvEMfCG//2gAMAwEAAgADAAAAEHwv/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qp//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/EIf/xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhMUFRYaH/2gAIAQEAAT8QzmV0unBuV0EX3Bsd+QXjwSyji/Y501BtdwgmZA0dfWIkZtA5n//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Image CDNs&quot;
        title=&quot;Image CDNs&quot;
        src=&quot;/static/44fbf1af203824c13400c077cda2dae8/6a068/img-cdn-02.jpg&quot;
        srcset=&quot;/static/44fbf1af203824c13400c077cda2dae8/09b79/img-cdn-02.jpg 240w,
/static/44fbf1af203824c13400c077cda2dae8/7cc5e/img-cdn-02.jpg 480w,
/static/44fbf1af203824c13400c077cda2dae8/6a068/img-cdn-02.jpg 960w,
/static/44fbf1af203824c13400c077cda2dae8/644c5/img-cdn-02.jpg 1440w,
/static/44fbf1af203824c13400c077cda2dae8/0f98f/img-cdn-02.jpg 1920w,
/static/44fbf1af203824c13400c077cda2dae8/12609/img-cdn-02.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lazy load images&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lazy loading images means that images are only loaded when they are in the viewport. This can reduce contention for network bandwidth by ensuring that images are only loaded when they are needed.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8b4282912cc5c5c55a3997758fd1163e/12609/lazy-img-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEEAgP/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAG1X4EdA//EABoQAQACAwEAAAAAAAAAAAAAAAIBAwASExH/2gAIAQEAAQUCas6bXe0qUJBnOY2BgR//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAeEAACAQMFAAAAAAAAAAAAAAAAAQIRIXEDEhMxgf/aAAgBAQAGPwJpTgvBLl064LyjLA7die25SKof/8QAGRABAQEBAQEAAAAAAAAAAAAAAREAMSFB/9oACAEBAAE/ITCQ+KuhCooVzWDl7zlkm+FyyBBLngBbDf/aAAwDAQACAAMAAAAQ0w//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAQEBAAMAAAAAAAAAAAABEQAhQTFxkf/aAAgBAQABPxBVYKPAl7lUoVpxbPozczZNI5zKfnxdST5z9MP1JJ+ZO5fgu//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Lazy Load Images&quot;
        title=&quot;Lazy Load Images&quot;
        src=&quot;/static/8b4282912cc5c5c55a3997758fd1163e/6a068/lazy-img-01.jpg&quot;
        srcset=&quot;/static/8b4282912cc5c5c55a3997758fd1163e/09b79/lazy-img-01.jpg 240w,
/static/8b4282912cc5c5c55a3997758fd1163e/7cc5e/lazy-img-01.jpg 480w,
/static/8b4282912cc5c5c55a3997758fd1163e/6a068/lazy-img-01.jpg 960w,
/static/8b4282912cc5c5c55a3997758fd1163e/644c5/lazy-img-01.jpg 1440w,
/static/8b4282912cc5c5c55a3997758fd1163e/0f98f/lazy-img-01.jpg 1920w,
/static/8b4282912cc5c5c55a3997758fd1163e/12609/lazy-img-01.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/66cc98c607e5ccad55d49e9ad4882909/12609/lazy-img-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQADBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHtGjBNX//EABkQAAEFAAAAAAAAAAAAAAAAACEBEBESMf/aAAgBAQABBQIyapj/AP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABcQAAMBAAAAAAAAAAAAAAAAAAACIDH/2gAIAQEABj8CY2f/xAAZEAEAAwEBAAAAAAAAAAAAAAABABEhQVH/2gAIAQEAAT8h93MlkrX2NS3c6xBKTIZk/9oADAMBAAIAAwAAABA/z//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABcRAAMBAAAAAAAAAAAAAAAAAAEQESH/2gAIAQIBAT8QNmL/xAAaEAEAAwEBAQAAAAAAAAAAAAABABExIUFh/9oACAEBAAE/ED4FsZALKXZN7yMAWLGqmMayl5XjOFAA4BP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Lazy Load Images&quot;
        title=&quot;Lazy Load Images&quot;
        src=&quot;/static/66cc98c607e5ccad55d49e9ad4882909/6a068/lazy-img-02.jpg&quot;
        srcset=&quot;/static/66cc98c607e5ccad55d49e9ad4882909/09b79/lazy-img-02.jpg 240w,
/static/66cc98c607e5ccad55d49e9ad4882909/7cc5e/lazy-img-02.jpg 480w,
/static/66cc98c607e5ccad55d49e9ad4882909/6a068/lazy-img-02.jpg 960w,
/static/66cc98c607e5ccad55d49e9ad4882909/644c5/lazy-img-02.jpg 1440w,
/static/66cc98c607e5ccad55d49e9ad4882909/0f98f/lazy-img-02.jpg 1920w,
/static/66cc98c607e5ccad55d49e9ad4882909/12609/lazy-img-02.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/aebcf194195829259e2d7527673d01d4/12609/lazy-img-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7NUA//xAAXEAEAAwAAAAAAAAAAAAAAAAABESAx/9oACAEBAAEFAnZa/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAIDH/2gAIAQEABj8CIv8A/8QAGhAAAgIDAAAAAAAAAAAAAAAAAAEQoREhUf/aAAgBAQABPyHJaKyp2GKP/9oADAMBAAIAAwAAABBDD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQADAQADAAAAAAAAAAAAAAEAESExEEFx/9oACAEBAAE/EKBFxdpvqYOeX7enhNzRpLdn/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Lazy Load Images&quot;
        title=&quot;Lazy Load Images&quot;
        src=&quot;/static/aebcf194195829259e2d7527673d01d4/6a068/lazy-img-03.jpg&quot;
        srcset=&quot;/static/aebcf194195829259e2d7527673d01d4/09b79/lazy-img-03.jpg 240w,
/static/aebcf194195829259e2d7527673d01d4/7cc5e/lazy-img-03.jpg 480w,
/static/aebcf194195829259e2d7527673d01d4/6a068/lazy-img-03.jpg 960w,
/static/aebcf194195829259e2d7527673d01d4/644c5/lazy-img-03.jpg 1440w,
/static/aebcf194195829259e2d7527673d01d4/0f98f/lazy-img-03.jpg 1920w,
/static/aebcf194195829259e2d7527673d01d4/12609/lazy-img-03.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;LCP is complex, and its timing can be affected by a number of factors. But if you consider that optimizing LCP is primarily about optimizing the load of the LCP resource, it can significantly simplify things.&lt;/p&gt;
&lt;p&gt;At a high level, optimizing LCP can be summarized in a few steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the LCP resource starts loading as early as possible.&lt;/li&gt;
&lt;li&gt;Ensure the LCP element can render as soon as its resource finishes loading.&lt;/li&gt;
&lt;li&gt;Reduce the load time of the LCP resource as much as you can without sacrificing quality.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/courses/web-perf&quot;&gt;Web Performance Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/optimize-lcp&quot;&gt;web.dev Optimize Largest Contentful Paint&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[How to change VSCode terminal font?]]></title><link>https://trungvose.comvs-code-terminal-font/</link><guid isPermaLink="false">https://trungvose.comvs-code-terminal-font/</guid><pubDate>Mon, 30 Oct 2023 17:32:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;So, I had to reinstall my computer and set up the terminal all over again. Here’s what I typically need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iTerm (That’s the one most of my gang use)&lt;/li&gt;
&lt;li&gt;Oh My Zsh&lt;/li&gt;
&lt;li&gt;powerlevel10k&lt;/li&gt;
&lt;li&gt;zsh-syntax-highlighting&lt;/li&gt;
&lt;li&gt;zsh-autosuggestions&lt;/li&gt;
&lt;li&gt;fzf&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ll give you a step-by-step on that soon. But for now, iTerm looks great.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/45b3bc32f79beceba0f33e73f70388e5/670dc/01-iterm.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcyUFCD/xAAXEAADAQAAAAAAAAAAAAAAAAAQERIg/9oACAEBAAEFAqDx/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGBAAAgMAAAAAAAAAAAAAAAAAARAgMUH/2gAIAQEABj8CoLIf/8QAGxAAAgEFAAAAAAAAAAAAAAAAAAERECFBceH/2gAIAQEAAT8hlwG5ZdhNKqP/2gAMAwEAAgADAAAAEAPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQARECExYXH/2gAIAQEAAT8QUb2y9yB0cRbt8AZNk//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;iTerm&quot;
        title=&quot;iTerm&quot;
        src=&quot;/static/45b3bc32f79beceba0f33e73f70388e5/6a068/01-iterm.jpg&quot;
        srcset=&quot;/static/45b3bc32f79beceba0f33e73f70388e5/09b79/01-iterm.jpg 240w,
/static/45b3bc32f79beceba0f33e73f70388e5/7cc5e/01-iterm.jpg 480w,
/static/45b3bc32f79beceba0f33e73f70388e5/6a068/01-iterm.jpg 960w,
/static/45b3bc32f79beceba0f33e73f70388e5/644c5/01-iterm.jpg 1440w,
/static/45b3bc32f79beceba0f33e73f70388e5/670dc/01-iterm.jpg 1650w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, the VSCode terminal doesn’t look right. The font doesn’t load properly, and where I expect to see icons, there are question marks instead.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2d242481dbc82db52bb69aeb6b1885eb/2e29f/02-vscode.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHkZdZBcf/EABcQAQEBAQAAAAAAAAAAAAAAABEAARD/2gAIAQEAAQUCemRkX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AYj/xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAACAgMAAAAAAAAAAAAAAAAAARFhEFHw/9oACAEBAAE/IZcsSyhFCIaR/9oADAMBAAIAAwAAABAnH//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EKr/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/EA//xAAcEAEAAgEFAAAAAAAAAAAAAAABACHBETFh4fH/2gAIAQEAAT8QrSr4QURNyLPU8qWYo6uKf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;VSCode&quot;
        title=&quot;VSCode&quot;
        src=&quot;/static/2d242481dbc82db52bb69aeb6b1885eb/6a068/02-vscode.jpg&quot;
        srcset=&quot;/static/2d242481dbc82db52bb69aeb6b1885eb/09b79/02-vscode.jpg 240w,
/static/2d242481dbc82db52bb69aeb6b1885eb/7cc5e/02-vscode.jpg 480w,
/static/2d242481dbc82db52bb69aeb6b1885eb/6a068/02-vscode.jpg 960w,
/static/2d242481dbc82db52bb69aeb6b1885eb/644c5/02-vscode.jpg 1440w,
/static/2d242481dbc82db52bb69aeb6b1885eb/0f98f/02-vscode.jpg 1920w,
/static/2d242481dbc82db52bb69aeb6b1885eb/2e29f/02-vscode.jpg 2421w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This guide will help you fix the font issue in the VSCode terminal.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;h3 id=&quot;step-1-open-settingsjson-in-vscode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-open-settingsjson-in-vscode&quot; aria-label=&quot;step 1 open settingsjson in vscode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1. Open settings.json in VSCode&lt;/h3&gt;
&lt;p&gt;Hit Command ⌘ + Shift ⇧ + P in VSCode, type &lt;code class=&quot;language-text&quot;&gt;settings&lt;/code&gt; and select &lt;code class=&quot;language-text&quot;&gt;Preferences: Open User Settings (JSON)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c0243c201603a60bcd80ce3ab04fc574/33042/solve-01-setting.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAQACBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeYGQiP/xAAZEAACAwEAAAAAAAAAAAAAAAAAARARMUH/2gAIAQEAAQUCWss7H//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABUQAQEAAAAAAAAAAAAAAAAAAAEg/9oACAEBAAY/Amv/xAAYEAEBAQEBAAAAAAAAAAAAAAABABFREP/aAAgBAQABPyFwk5a6w4pd8//aAAwDAQACAAMAAAAQG8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAACAgIDAAAAAAAAAAAAAAAAAREhEDFBgfH/2gAIAQEAAT8QZA7U8je+iR6wyDGx22P/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;VSCode Preference&quot;
        title=&quot;VSCode Preference&quot;
        src=&quot;/static/c0243c201603a60bcd80ce3ab04fc574/6a068/solve-01-setting.jpg&quot;
        srcset=&quot;/static/c0243c201603a60bcd80ce3ab04fc574/09b79/solve-01-setting.jpg 240w,
/static/c0243c201603a60bcd80ce3ab04fc574/7cc5e/solve-01-setting.jpg 480w,
/static/c0243c201603a60bcd80ce3ab04fc574/6a068/solve-01-setting.jpg 960w,
/static/c0243c201603a60bcd80ce3ab04fc574/644c5/solve-01-setting.jpg 1440w,
/static/c0243c201603a60bcd80ce3ab04fc574/0f98f/solve-01-setting.jpg 1920w,
/static/c0243c201603a60bcd80ce3ab04fc574/33042/solve-01-setting.jpg 2070w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-2-add-this-code-into-settingsjson&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-add-this-code-into-settingsjson&quot; aria-label=&quot;step 2 add this code into settingsjson permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2. Add this code into settings.json&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt; &lt;span class=&quot;token property&quot;&gt;&quot;terminal.integrated.fontFamily&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MesloLGS NF&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;token property&quot;&gt;&quot;terminal.integrated.defaultProfile.osx&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;zsh&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;token property&quot;&gt;&quot;terminal.external.osxExec&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;iTerm.app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you’re wondering where I got &lt;code class=&quot;language-text&quot;&gt;MesloLGS NF&lt;/code&gt; from - You can find it on iTerm’s setting.&lt;/p&gt;
&lt;p&gt;Just go to &lt;code class=&quot;language-text&quot;&gt;iTerm -&gt; Settings -&gt; Profiles -&gt; Text -&gt; Font&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/30508f9d955dca9611d49e0e19e2c966/1176d/solve-02-iterm-setting.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHnDRDjV//EABcQAQEBAQAAAAAAAAAAAAAAABEAARD/2gAIAQEAAQUCSZt7/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAASD/2gAIAQEABj8Ca//EABkQAQEBAAMAAAAAAAAAAAAAAAEAMRARQf/aAAgBAQABPyFyGy2H15Y4L//aAAwDAQACAAMAAAAQDw//xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QTJ//xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQIBAT8QHZ//xAAdEAABBAIDAAAAAAAAAAAAAAAAAREhMRBRcYGx/9oACAEBAAE/ENgMuRCaE4IxuiHvih//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;VSCode Preference&quot;
        title=&quot;VSCode Preference&quot;
        src=&quot;/static/30508f9d955dca9611d49e0e19e2c966/6a068/solve-02-iterm-setting.jpg&quot;
        srcset=&quot;/static/30508f9d955dca9611d49e0e19e2c966/09b79/solve-02-iterm-setting.jpg 240w,
/static/30508f9d955dca9611d49e0e19e2c966/7cc5e/solve-02-iterm-setting.jpg 480w,
/static/30508f9d955dca9611d49e0e19e2c966/6a068/solve-02-iterm-setting.jpg 960w,
/static/30508f9d955dca9611d49e0e19e2c966/644c5/solve-02-iterm-setting.jpg 1440w,
/static/30508f9d955dca9611d49e0e19e2c966/0f98f/solve-02-iterm-setting.jpg 1920w,
/static/30508f9d955dca9611d49e0e19e2c966/1176d/solve-02-iterm-setting.jpg 2352w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a67e5203c05cafc831748522cef0018f/17460/solve-03-iterm-profile-font.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHk6KiBB//EABcQAAMBAAAAAAAAAAAAAAAAAAAQEQH/2gAIAQEAAQUCpCLV/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEQESEx/9oACAEBAAE/IehhiluDR//aAAwDAQACAAMAAAAQQ8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFBgaH/2gAIAQEAAT8QLAruAtB8uEPCeQodLHUTOWK7n//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;VSCode Preference&quot;
        title=&quot;VSCode Preference&quot;
        src=&quot;/static/a67e5203c05cafc831748522cef0018f/6a068/solve-03-iterm-profile-font.jpg&quot;
        srcset=&quot;/static/a67e5203c05cafc831748522cef0018f/09b79/solve-03-iterm-profile-font.jpg 240w,
/static/a67e5203c05cafc831748522cef0018f/7cc5e/solve-03-iterm-profile-font.jpg 480w,
/static/a67e5203c05cafc831748522cef0018f/6a068/solve-03-iterm-profile-font.jpg 960w,
/static/a67e5203c05cafc831748522cef0018f/644c5/solve-03-iterm-profile-font.jpg 1440w,
/static/a67e5203c05cafc831748522cef0018f/17460/solve-03-iterm-profile-font.jpg 1886w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-3-save--restart-vscode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-save--restart-vscode&quot; aria-label=&quot;step 3 save  restart vscode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3. Save &amp;#x26; Restart VSCode&lt;/h3&gt;
&lt;p&gt;And voila! Your VSCode terminal should now have a new font.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/143c0fa502b8fee4c712b45ba735739e/2ed8b/03-vscode-final.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAMF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAchVZFYf/8QAFxABAAMAAAAAAAAAAAAAAAAAAAERE//aAAgBAQABBQK1rZyzllL/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwGq/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8BiP/EABYQAQEBAAAAAAAAAAAAAAAAAEEAEP/aAAgBAQAGPwI0iL//xAAbEAACAgMBAAAAAAAAAAAAAAAAARFhQVGBkf/aAAgBAQABPyGevA2euIcnjiLxeLR//9oADAMBAAIAAwAAABCHH//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EIQ//8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QpT//xAAZEAEAAgMAAAAAAAAAAAAAAAABANERcfH/2gAIAQEAAT8QCoxnKDQRsyDQTsNTuNQWxqf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;VSCode Terminal&quot;
        title=&quot;VSCode Terminal&quot;
        src=&quot;/static/143c0fa502b8fee4c712b45ba735739e/6a068/03-vscode-final.jpg&quot;
        srcset=&quot;/static/143c0fa502b8fee4c712b45ba735739e/09b79/03-vscode-final.jpg 240w,
/static/143c0fa502b8fee4c712b45ba735739e/7cc5e/03-vscode-final.jpg 480w,
/static/143c0fa502b8fee4c712b45ba735739e/6a068/03-vscode-final.jpg 960w,
/static/143c0fa502b8fee4c712b45ba735739e/644c5/03-vscode-final.jpg 1440w,
/static/143c0fa502b8fee4c712b45ba735739e/2ed8b/03-vscode-final.jpg 1648w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[@next/bundle-analyzer throw error Module not found: Can't resolve child_process]]></title><link>https://trungvose.comnext-bundle-analyzer-cant-resolve-child-process/</link><guid isPermaLink="false">https://trungvose.comnext-bundle-analyzer-cant-resolve-child-process/</guid><pubDate>Sat, 16 Sep 2023 08:32:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Recently I helped my friend to analyze why his Next.js project is slow. I installed &lt;code class=&quot;language-text&quot;&gt;@next/bundle-analyzer&lt;/code&gt; which can help us to analyze the bundle size. I follow their guideline for configuration&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;yarn add @next/bundle-analyzer&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update &lt;code class=&quot;language-text&quot;&gt;next.config.js&lt;/code&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @type {import(&apos;next&apos;).NextConfig} */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nextConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;reactStrictMode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;i18n&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;locales&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;en&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;vi&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;defaultLocale&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;vi&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;localeDetection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; withBundleAnalyzer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;@next/bundle-analyzer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ANALYZE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;true&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withBundleAnalyzer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Finally, run &lt;code class=&quot;language-text&quot;&gt;ANALYZE=true yarn build&lt;/code&gt;, it throws an error:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yarn run v1.22.19
$ next build
- info Loaded env from /root/Frontend/.env.production
- info Loaded env from /root/Frontend/.env
- info Linting and checking validity of types
Failed to compile.

./node_modules/opener/lib/opener.js
Module not found: Can&apos;t resolve &apos;child_process&apos;

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/webpack-bundle-analyzer/lib/utils.js
./node_modules/webpack-bundle-analyzer/lib/viewer.js
./node_modules/webpack-bundle-analyzer/lib/index.js
./node_modules/@next/bundle-analyzer/index.js
./next.config.js
./hooks/useAnalyzeUrl.js
./PageComponents/AnalyzePage/AnalyzePage.js
./pages/analyze/index.js


&gt; Build failed because of webpack errors
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;I found a solution from &lt;a href=&quot;https://stackoverflow.com/a/71548920/3375906&quot;&gt;here&lt;/a&gt;. Basically we need to disable &lt;code class=&quot;language-text&quot;&gt;child_process&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;fs&lt;/code&gt; in webpack config. Here is the final &lt;code class=&quot;language-text&quot;&gt;next.config.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;/** @type {import(&apos;next&apos;).NextConfig} */
const nextConfig = {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; reactStrictMode: true,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; swcMinify: true,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; compress: false,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; i18n: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   locales: [&apos;en&apos;, &apos;vi&apos;],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   defaultLocale: &apos;vi&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   localeDetection: false,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; },
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  webpack: (config, { isServer }) =&gt; {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    if (!isServer) {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      config.resolve.fallback.fs = false
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      config.resolve.fallback.child_process = false
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    }
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    return config
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  },
&lt;/span&gt;}

const withBundleAnalyzer = require(&apos;@next/bundle-analyzer&apos;)({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; enabled: process.env.ANALYZE === &apos;true&apos;,
&lt;/span&gt;})

module.exports = withBundleAnalyzer(nextConfig)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Angular Material 15 Migration]]></title><link>https://trungvose.comangular-material-15-migration/</link><guid isPermaLink="false">https://trungvose.comangular-material-15-migration/</guid><pubDate>Sun, 03 Sep 2023 08:32:00 GMT</pubDate><content:encoded>&lt;p&gt;Angular Material 15 migration is part of our plan to migrate to Angular 15. It was successfully completed a few months ago, and we are now using Angular 15 in production. This document provides a step-by-step guide on how we migrated Angular Material to version 15, which involved migrating to MDC-based Angular Material components.&lt;/p&gt;
&lt;p&gt;Many of the components in Angular Material v15 have been refactored to be based on Angular Material Design Components (MDC) for the Web. The refactored components offer improved accessibility and adherence to the Material Design spec.&lt;/p&gt;
&lt;h2 id=&quot;what-has-changed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-has-changed&quot; aria-label=&quot;what has changed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What has changed?&lt;/h2&gt;
&lt;p&gt;The new components have different internal DOM and CSS styles. Since the MDC-based components are not API-compatible with the previous versions, you will need to update your code to use the new components. We follow two steps as mentioned on the &lt;a href=&quot;https://material.angular.io/guide/mdc-migration&quot;&gt;Migrating to MDC-based Angular Material Components&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;step-1-update-to-angular-material-v15&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-update-to-angular-material-v15&quot; aria-label=&quot;step 1 update to angular material v15 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1. Update to Angular Material v15&lt;/h2&gt;
&lt;p&gt;Angular Material includes a schematic to help migrate applications to use the new MDC-based components. To get started, upgrade your application to Angular Material 15.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ng update @angular/material@15&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As part of this update, a schematic will run to automatically move your application to use the “legacy” imports containing the old component implementations. This provides a quick path to getting your application running on v15 with minimal manual changes. After the update, it should look like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a19b5677a7848e67ab722580047f87f6/9c5f4/angular-material-15-migration-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAHJqJAb/8QAFhAAAwAAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAEFAhz/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAXEAADAQAAAAAAAAAAAAAAAAAAEDGB/9oACAEBAAE/IZTS/9oADAMBAAIAAwAAABAID//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAgMAAAAAAAAAAAAAAAEAESFBUZGh/9oACAEBAAE/ELEl11TELj1C+Xuf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/a19b5677a7848e67ab722580047f87f6/6a068/angular-material-15-migration-01.jpg&quot;
        srcset=&quot;/static/a19b5677a7848e67ab722580047f87f6/09b79/angular-material-15-migration-01.jpg 240w,
/static/a19b5677a7848e67ab722580047f87f6/7cc5e/angular-material-15-migration-01.jpg 480w,
/static/a19b5677a7848e67ab722580047f87f6/6a068/angular-material-15-migration-01.jpg 960w,
/static/a19b5677a7848e67ab722580047f87f6/644c5/angular-material-15-migration-01.jpg 1440w,
/static/a19b5677a7848e67ab722580047f87f6/9c5f4/angular-material-15-migration-01.jpg 1806w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We then commit the changes and push to the remote repository. Everything should work as before, nothing changes.&lt;/p&gt;
&lt;h2 id=&quot;step-2-update-to-mdc-based-components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-update-to-mdc-based-components&quot; aria-label=&quot;step 2 update to mdc based components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2. Update to MDC-based components&lt;/h2&gt;
&lt;p&gt;Then we schedule to update to MDC-based components. This is a manual process that involves updating your application to use the new component implementations.&lt;/p&gt;
&lt;p&gt;First, we run the migration command to switch from the legacy component implementations to the new MDC-based ones.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ng generate @angular/material:mdc-migration&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9bd2f1bf3667985ae06650c0f0118999/70d6d/angular-material-15-migration-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABx5AD/8QAFRABAQAAAAAAAAAAAAAAAAAAARD/2gAIAQEAAQUCW//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABgQAAIDAAAAAAAAAAAAAAAAAAERABAh/9oACAEBAAE/ISM7HX//2gAMAwEAAgADAAAAEHPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAFxABAQEBAAAAAAAAAAAAAAAAEQABUf/aAAgBAQABPxAAtxl2dv/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/9bd2f1bf3667985ae06650c0f0118999/6a068/angular-material-15-migration-02.jpg&quot;
        srcset=&quot;/static/9bd2f1bf3667985ae06650c0f0118999/09b79/angular-material-15-migration-02.jpg 240w,
/static/9bd2f1bf3667985ae06650c0f0118999/7cc5e/angular-material-15-migration-02.jpg 480w,
/static/9bd2f1bf3667985ae06650c0f0118999/6a068/angular-material-15-migration-02.jpg 960w,
/static/9bd2f1bf3667985ae06650c0f0118999/644c5/angular-material-15-migration-02.jpg 1440w,
/static/9bd2f1bf3667985ae06650c0f0118999/0f98f/angular-material-15-migration-02.jpg 1920w,
/static/9bd2f1bf3667985ae06650c0f0118999/70d6d/angular-material-15-migration-02.jpg 2398w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/57f478862bfaf4f4715fa4714461df47/afdf6/angular-material-15-migration-08.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.916666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAEABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABx5AD/8QAFRABAQAAAAAAAAAAAAAAAAAAARD/2gAIAQEAAQUCW//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABYQAQEBAAAAAAAAAAAAAAAAAAEQEf/aAAgBAQABPyFNb//aAAwDAQACAAMAAAAQ88//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAXEAEBAQEAAAAAAAAAAAAAAAABEQDw/9oACAEBAAE/EIqgWQDV41eN/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/57f478862bfaf4f4715fa4714461df47/6a068/angular-material-15-migration-08.jpg&quot;
        srcset=&quot;/static/57f478862bfaf4f4715fa4714461df47/09b79/angular-material-15-migration-08.jpg 240w,
/static/57f478862bfaf4f4715fa4714461df47/7cc5e/angular-material-15-migration-08.jpg 480w,
/static/57f478862bfaf4f4715fa4714461df47/6a068/angular-material-15-migration-08.jpg 960w,
/static/57f478862bfaf4f4715fa4714461df47/644c5/angular-material-15-migration-08.jpg 1440w,
/static/57f478862bfaf4f4715fa4714461df47/0f98f/angular-material-15-migration-08.jpg 1920w,
/static/57f478862bfaf4f4715fa4714461df47/afdf6/angular-material-15-migration-08.jpg 4350w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Upon running the command, you will be prompted to select which components you would like to migrate. We divided into three steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Form Field, Input&lt;/li&gt;
&lt;li&gt;Select&lt;/li&gt;
&lt;li&gt;AutoComplete&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;why-is-it-necessary-to-break-the-migration-of-angular-material-into-three-parts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-is-it-necessary-to-break-the-migration-of-angular-material-into-three-parts&quot; aria-label=&quot;why is it necessary to break the migration of angular material into three parts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why is it necessary to break the migration of Angular Material into three parts?&lt;/h3&gt;
&lt;p&gt;This is because deep down inside the &lt;code class=&quot;language-text&quot;&gt;MatSelectModule&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;MatAutoCompleteModule&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;MatInputModule&lt;/code&gt;, they all import the &lt;code class=&quot;language-text&quot;&gt;MatFormField&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;By breaking the migration into three parts, with the following modules are being migrated one by one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1️⃣ &lt;code class=&quot;language-text&quot;&gt;MatFormFieldModule&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;MatInputModule&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;2️⃣ &lt;code class=&quot;language-text&quot;&gt;MatSelectModule&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;3️⃣ &lt;code class=&quot;language-text&quot;&gt;MatAutoCompleteModule&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We avoid a declaration conflict by doing so.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fd9a6f6767c870a8aac1f98447479055/d8d7f/why-three-steps-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.666666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHLU0VAH//EABcQAAMBAAAAAAAAAAAAAAAAAAABEEH/2gAIAQEAAQUCHMn/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAaEAACAgMAAAAAAAAAAAAAAAABEQAQITFh/9oACAEBAAE/IQ1yAVihQ7n/2gAMAwEAAgADAAAAEITP/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QJ//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGf/xAAcEAEBAAEFAQAAAAAAAAAAAAABADERIUFRYXH/2gAIAQEAAT8QGo9rIH4SJkt7zPJezf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Why breaking Angular Material migration to three parts&quot;
        title=&quot;Why breaking Angular Material migration to three parts&quot;
        src=&quot;/static/fd9a6f6767c870a8aac1f98447479055/6a068/why-three-steps-01.jpg&quot;
        srcset=&quot;/static/fd9a6f6767c870a8aac1f98447479055/09b79/why-three-steps-01.jpg 240w,
/static/fd9a6f6767c870a8aac1f98447479055/7cc5e/why-three-steps-01.jpg 480w,
/static/fd9a6f6767c870a8aac1f98447479055/6a068/why-three-steps-01.jpg 960w,
/static/fd9a6f6767c870a8aac1f98447479055/d8d7f/why-three-steps-01.jpg 1032w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 836px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07d19508e60a706a885407a6f95460dc/49e4f/why-three-steps-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.916666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAEABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAEEBf/EABYBAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAABy65OsA//xAAXEAADAQAAAAAAAAAAAAAAAAABAhAh/9oACAEBAAEFAjiz/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAEQETH/2gAIAQEAAT8h2ouP/9oADAMBAAIAAwAAABAL7//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/EGf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAACAgMBAAAAAAAAAAAAAAAAAREhMVFxkf/aAAgBAQABPxCzmbh0cr1kvbP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Why breaking Angular Material migration to three parts&quot;
        title=&quot;Why breaking Angular Material migration to three parts&quot;
        src=&quot;/static/07d19508e60a706a885407a6f95460dc/49e4f/why-three-steps-02.jpg&quot;
        srcset=&quot;/static/07d19508e60a706a885407a6f95460dc/09b79/why-three-steps-02.jpg 240w,
/static/07d19508e60a706a885407a6f95460dc/7cc5e/why-three-steps-02.jpg 480w,
/static/07d19508e60a706a885407a6f95460dc/49e4f/why-three-steps-02.jpg 836w&quot;
        sizes=&quot;(max-width: 836px) 100vw, 836px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;todomdc-migration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#todomdc-migration&quot; aria-label=&quot;todomdc migration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TODO(mdc-migration)&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ng generate @angular/material:mdc-migration&lt;/code&gt; command updates your styles and templates to the new implementations as much as it can automatically.&lt;/p&gt;
&lt;p&gt;Changes that are deprecated and require modification will have the prefix &lt;code class=&quot;language-text&quot;&gt;/* TODO(mdc-migration):&lt;/code&gt;. Our job will be to fix them all, one by one.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;/* TODO(mdc-migration):&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/12869b14ef4839a7c25a03e8b292a858/46d48/angular-material-15-migration-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 166.66666666666669%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAhABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAEDBAIF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQAC/9oADAMBAAIQAxAAAAHy69lNVJRor0ZR5CWQGuAn/8QAHBAAAgICAwAAAAAAAAAAAAAAAAECEhARIjFC/9oACAEBAAEFAqtrEFwkq4jWmxkN0fZ5Gf/EABYRAAMAAAAAAAAAAAAAAAAAAAEQIP/aAAgBAwEBPwGCv//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BX//EABoQAAICAwAAAAAAAAAAAAAAAAEQITEAIDL/2gAIAQEABj8CpiA5ygh1t//EAB4QAAICAgIDAAAAAAAAAAAAAAERACExQRBRYXHw/9oACAEBAAE/IQEIcdrlpn1tw5XvqGEhC1Pk5lCraaUBWF+YYMfXGU//2gAMAwEAAgADAAAAEHQw84AP/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAg/9oACAEDAQE/EEjh/8QAFhEBAQEAAAAAAAAAAAAAAAAAEAER/9oACAECAQE/ENIQ/8QAIhAAAQQBAwUBAAAAAAAAAAAAAQARITFBUXGREGGBwdHw/9oACAEBAAE/ECpyBBYGp2RDgH0iiHFpDwgh4FgMOVJjcZQ7qsMfUx4b/NUWQnsonbSrw4RiAQAyLLBtMdJFKrZf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/12869b14ef4839a7c25a03e8b292a858/6a068/angular-material-15-migration-03.jpg&quot;
        srcset=&quot;/static/12869b14ef4839a7c25a03e8b292a858/09b79/angular-material-15-migration-03.jpg 240w,
/static/12869b14ef4839a7c25a03e8b292a858/7cc5e/angular-material-15-migration-03.jpg 480w,
/static/12869b14ef4839a7c25a03e8b292a858/6a068/angular-material-15-migration-03.jpg 960w,
/static/12869b14ef4839a7c25a03e8b292a858/46d48/angular-material-15-migration-03.jpg 1098w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;key-changes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-changes&quot; aria-label=&quot;key changes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key changes&lt;/h3&gt;
&lt;p&gt;Below is what happened after I ran the migration script.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f0482b98e89baaacf0e528d4ec273ce0/c337d/angular-material-15-migration-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3qEUf//EABYQAQEBAAAAAAAAAAAAAAAAABEQIP/aAAgBAQABBQIi4//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAABAgIf/aAAgBAQAGPwIRP//EABkQAQACAwAAAAAAAAAAAAAAAAEQESAhcf/aAAgBAQABPyHSAC0PMP/aAAwDAQACAAMAAAAQc8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEBAQADAAAAAAAAAAAAAAABERAAUXH/2gAIAQEAAT8QBUXJQnaugBADzn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/f0482b98e89baaacf0e528d4ec273ce0/6a068/angular-material-15-migration-04.jpg&quot;
        srcset=&quot;/static/f0482b98e89baaacf0e528d4ec273ce0/09b79/angular-material-15-migration-04.jpg 240w,
/static/f0482b98e89baaacf0e528d4ec273ce0/7cc5e/angular-material-15-migration-04.jpg 480w,
/static/f0482b98e89baaacf0e528d4ec273ce0/6a068/angular-material-15-migration-04.jpg 960w,
/static/f0482b98e89baaacf0e528d4ec273ce0/644c5/angular-material-15-migration-04.jpg 1440w,
/static/f0482b98e89baaacf0e528d4ec273ce0/0f98f/angular-material-15-migration-04.jpg 1920w,
/static/f0482b98e89baaacf0e528d4ec273ce0/c337d/angular-material-15-migration-04.jpg 2204w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dimensions are now different. Everything is now bigger, from height, width to paddings, margins, and animation durations.
&lt;ul&gt;
&lt;li&gt;For textbox heights, we need to revert back to the magical &lt;code class=&quot;language-text&quot;&gt;67px&lt;/code&gt; that exists in the previous version. The width of the elements remains unchanged.&lt;/li&gt;
&lt;li&gt;For paddings, horizontal paddings are now &lt;code class=&quot;language-text&quot;&gt;16px&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;8px&lt;/code&gt; like it used to be.&lt;/li&gt;
&lt;li&gt;Animation durations of the labels are now faster, &lt;code class=&quot;language-text&quot;&gt;150ms&lt;/code&gt; by default. We need to change it back to &lt;code class=&quot;language-text&quot;&gt;400ms&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Default required marker “ are now always enabled by default. We need to hide it by adding the following lines in our app.module.ts.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   provide&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAT_FORM_FIELD_DEFAULT_OPTIONS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
   useValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      hideRequiredMarker&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;manually-fixing-the-style-changes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#manually-fixing-the-style-changes&quot; aria-label=&quot;manually fixing the style changes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Manually fixing the style changes&lt;/h3&gt;
&lt;p&gt;The main changes we’ll need to fix will mostly be style changes. After running the migrations, most places where we use the &lt;code class=&quot;language-text&quot;&gt;mat-form-field&lt;/code&gt; component will be broken, as we heavily customize using CSS selector to meet our needs, e.g &lt;code class=&quot;language-text&quot;&gt;.mat-select&lt;/code&gt; now become &lt;code class=&quot;language-text&quot;&gt;.mat-mdc-select&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For instance, I had to make several changes such as:&lt;/p&gt;
&lt;p&gt;ℹ️ Update &lt;code class=&quot;language-text&quot;&gt;.mat-form-field-flex&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;.mat-mdc-text-field-wrapper&lt;/code&gt; selector:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/166b3ae84375f543487f768265d87679/b0376/angular-material-15-migration-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcmBAP/EABUQAQEAAAAAAAAAAAAAAAAAABAh/9oACAEBAAEFAo//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAXEAADAQAAAAAAAAAAAAAAAAAAASEx/9oACAEBAAE/IW6whD//2gAMAwEAAgADAAAAEHgv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAAIDAQAAAAAAAAAAAAAAAQARIUFxof/aAAgBAQABPxAFMs2KxXsOTv2f/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/166b3ae84375f543487f768265d87679/6a068/angular-material-15-migration-05.jpg&quot;
        srcset=&quot;/static/166b3ae84375f543487f768265d87679/09b79/angular-material-15-migration-05.jpg 240w,
/static/166b3ae84375f543487f768265d87679/7cc5e/angular-material-15-migration-05.jpg 480w,
/static/166b3ae84375f543487f768265d87679/6a068/angular-material-15-migration-05.jpg 960w,
/static/166b3ae84375f543487f768265d87679/644c5/angular-material-15-migration-05.jpg 1440w,
/static/166b3ae84375f543487f768265d87679/b0376/angular-material-15-migration-05.jpg 1784w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;ℹ️ &lt;code class=&quot;language-text&quot;&gt;mat-select&lt;/code&gt; now become &lt;code class=&quot;language-text&quot;&gt;.mat-mdc-select&lt;/code&gt; selector:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f5c5c1427d260c8bd7abeb90d79d6bfb/2c885/angular-material-15-migration-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.500000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcmBAP/EABUQAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAEFAm//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAWEAADAAAAAAAAAAAAAAAAAAAAASH/2gAIAQEAAT8hZVEIf//aAAwDAQACAAMAAAAQAA//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxCq/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGBAAAwEBAAAAAAAAAAAAAAAAABEhYQH/2gAIAQEAAT8Qapeseh6P/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/f5c5c1427d260c8bd7abeb90d79d6bfb/6a068/angular-material-15-migration-06.jpg&quot;
        srcset=&quot;/static/f5c5c1427d260c8bd7abeb90d79d6bfb/09b79/angular-material-15-migration-06.jpg 240w,
/static/f5c5c1427d260c8bd7abeb90d79d6bfb/7cc5e/angular-material-15-migration-06.jpg 480w,
/static/f5c5c1427d260c8bd7abeb90d79d6bfb/6a068/angular-material-15-migration-06.jpg 960w,
/static/f5c5c1427d260c8bd7abeb90d79d6bfb/644c5/angular-material-15-migration-06.jpg 1440w,
/static/f5c5c1427d260c8bd7abeb90d79d6bfb/2c885/angular-material-15-migration-06.jpg 1776w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;ℹ️ &lt;code class=&quot;language-text&quot;&gt;.mat-input-element&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;.mat-mdc-input-element&lt;/code&gt; selector:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f1c206a3f33824ed04c490e556ec49f5/03a73/angular-material-15-migration-07.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAHkwlNSL//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABoQAAICAwAAAAAAAAAAAAAAAAABETEQIVH/2gAIAQEAAT8hcTTHHDQqef/aAAwDAQACAAMAAAAQpz//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAACAQUAAAAAAAAAAAAAAAAAARAhMWFxof/aAAgBAQABPxBXauZCNGdA7R//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Update to Angular Material v15&quot;
        title=&quot;Update to Angular Material v15&quot;
        src=&quot;/static/f1c206a3f33824ed04c490e556ec49f5/6a068/angular-material-15-migration-07.jpg&quot;
        srcset=&quot;/static/f1c206a3f33824ed04c490e556ec49f5/09b79/angular-material-15-migration-07.jpg 240w,
/static/f1c206a3f33824ed04c490e556ec49f5/7cc5e/angular-material-15-migration-07.jpg 480w,
/static/f1c206a3f33824ed04c490e556ec49f5/6a068/angular-material-15-migration-07.jpg 960w,
/static/f1c206a3f33824ed04c490e556ec49f5/644c5/angular-material-15-migration-07.jpg 1440w,
/static/f1c206a3f33824ed04c490e556ec49f5/03a73/angular-material-15-migration-07.jpg 1782w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Not to mention a lot of places we need to compare the new implementation with the existing one and make changes accordingly.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope this article has given you a better understanding of how we moved to Angular Material 15 and the changes we made along the way. We successfully completed the migration to Angular Material 15 and are now using it in our production environment. Although the migration was challenging, it was definitely worth it. If you have any questions or comments, please feel free to leave them below!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Improve First Contentful Paint (FCP)]]></title><description><![CDATA[Learn how to improve First Contentful Paint (FCP)]]></description><link>https://trungvose.comweb-performance-improve-fcp/</link><guid isPermaLink="false">https://trungvose.comweb-performance-improve-fcp/</guid><pubDate>Mon, 14 Aug 2023 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Even though First Contentful Paint (FCP) is not a Core Web Vital, it is still an important metric to measure and improve. The Lighthouse report includes FCP as one of the metrics to measure web performance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f58b0d3ac6be75b12e12575b26181eff/cecd0/improve-fcp-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeXOUSD/xAAWEAADAAAAAAAAAAAAAAAAAAACEiD/2gAIAQEAAQUCcq//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAADAQAAAAAAAAAAAAAAAAABECAx/9oACAEBAAY/AtgL/8QAHBABAAIBBQAAAAAAAAAAAAAAAQAxEBEhQWFx/9oACAEBAAE/IRLU4tmvbCyJt4x//9oADAMBAAIAAwAAABCgD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQADAAMAAAAAAAAAAAAAAAEAETEhoeH/2gAIAQEAAT8QSLV7YS1GkF6TsEDDQseFqf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;First Contentful Paint&quot;
        title=&quot;First Contentful Paint&quot;
        src=&quot;/static/f58b0d3ac6be75b12e12575b26181eff/6a068/improve-fcp-01.jpg&quot;
        srcset=&quot;/static/f58b0d3ac6be75b12e12575b26181eff/09b79/improve-fcp-01.jpg 240w,
/static/f58b0d3ac6be75b12e12575b26181eff/7cc5e/improve-fcp-01.jpg 480w,
/static/f58b0d3ac6be75b12e12575b26181eff/6a068/improve-fcp-01.jpg 960w,
/static/f58b0d3ac6be75b12e12575b26181eff/644c5/improve-fcp-01.jpg 1440w,
/static/f58b0d3ac6be75b12e12575b26181eff/0f98f/improve-fcp-01.jpg 1920w,
/static/f58b0d3ac6be75b12e12575b26181eff/cecd0/improve-fcp-01.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;First Contentful Paint (FCP) measures the time from when the user first navigates to the page to when any part of the page’s content is rendered on the screen. Following FCP are other metrics like Largest Contentful Paint (LCP), which is part of the Core Web Vitals. Therefore, improving FCP will also improve LCP.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/269b57cbc4562fc1f98c9210fa755933/8c557/lcp-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACj0lEQVQoz02SS09TURRG+ydMlFeMQtrSYkBjok4cO9KJP0Anjh3IhLYY2nKBIqU+kGgkUUBMIOHVlttbagwMDIQIlLYBwyOUiC2EYu+j0CJd5pZoPMmXnX0e39n7nGW44G/i0lQzl8WnlAWaKAvaMEpOLorNVATtlAdtmCRXab5KtFMTbuZKRKBKdGCMOLg24+LmbAfmyFPMkgvDs7UIg9vzDGzP44pP0b4UoDf2mZcr0zQujNC2FOBVLEJXVOTBlyHujQ3zcPoDj2b7qenr49zzt5jf+6ge6MEUcmE4yKv8HZvKPrIiQ74Ix79ZTG+R13Jn+VGeaCqJ8uuYvHKEpsrc7v2KSZjjlneBKz0hjCEnhnVlr2RWLBb5lkmSPNxD0zRkVWFmd5WUnEHVVA6ULPPpDdScwlEux085g5hYZX17l9SezKf4IqZQC4YNZf+svCIsZpLsHO6T0zQUVS0ZprMZNFUjo2SZS22QVZXShT9+HTC3vUVOPqRYKDCxGcckOTGkjuR/LX/PpjlUslA4hfwJC+lNcnrLhVNOcsfE9nfguAD5UxRNRYxtEv2eZj2pML4Wxyy1YGiKTdK5No1nbZony6M4ViZpXRZxRad4vDhMa0zEEw3TlhBpXBqlPRHCk5BwxYPcDw9yZ2yI+oGP3I30U6tXWKmjEbCVVBWyU/XGjdHtpbrTQ8W4g3K/nfMjdip0bAIOykUble+cWDq6sXZ5aej2cbXLh/Gl5+xTLJIbq+RGj5awG0uwlbqAQO2oQIP3Bdc7e7jp7eVG12vqfD5qJRd1UwL1/nasE21YJ9tK0TIpYNINdRh16Q+qc2QOOzHpkpzU+lux+gWsAQGLXyjl+iF93RhuOdv3n3Sw/wCL6lPvwEkoXQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;First Contentful Paint&quot;
        title=&quot;First Contentful Paint&quot;
        src=&quot;/static/269b57cbc4562fc1f98c9210fa755933/8c557/lcp-01.png&quot;
        srcset=&quot;/static/269b57cbc4562fc1f98c9210fa755933/8ff5a/lcp-01.png 240w,
/static/269b57cbc4562fc1f98c9210fa755933/e85cb/lcp-01.png 480w,
/static/269b57cbc4562fc1f98c9210fa755933/8c557/lcp-01.png 700w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-good-fcp-score&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-a-good-fcp-score&quot; aria-label=&quot;what is a good fcp score permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is a good FCP score?&lt;/h2&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have a First Contentful Paint of &lt;code class=&quot;language-text&quot;&gt;1.8&lt;/code&gt; seconds or less. To ensure you’re hitting this target for most of your users, a good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bf00392165966c393bf4693e636fcf12/1b43a/fcp-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.666666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAEABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHdgiwr/8QAGBAAAgMAAAAAAAAAAAAAAAAAAAECIUH/2gAIAQEAAQUC1xFR/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8BZ//EABcQAAMBAAAAAAAAAAAAAAAAAAABEDH/2gAIAQEABj8CNc//xAAaEAADAAMBAAAAAAAAAAAAAAAAAREhMXGh/9oACAEBAAE/IWt8lHfQSJW+n//aAAwDAQACAAMAAAAQe9//xAAWEQADAAAAAAAAAAAAAAAAAAAAATH/2gAIAQMBAT8QVHT/xAAWEQEBAQAAAAAAAAAAAAAAAAAAATH/2gAIAQIBAT8Qyj//xAAaEAEBAAIDAAAAAAAAAAAAAAABEQAhMUGR/9oACAEBAAE/EDiuRqswEju4MMERB2q+5//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;First Contentful Paint&quot;
        title=&quot;First Contentful Paint&quot;
        src=&quot;/static/bf00392165966c393bf4693e636fcf12/6a068/fcp-score.jpg&quot;
        srcset=&quot;/static/bf00392165966c393bf4693e636fcf12/09b79/fcp-score.jpg 240w,
/static/bf00392165966c393bf4693e636fcf12/7cc5e/fcp-score.jpg 480w,
/static/bf00392165966c393bf4693e636fcf12/6a068/fcp-score.jpg 960w,
/static/bf00392165966c393bf4693e636fcf12/644c5/fcp-score.jpg 1440w,
/static/bf00392165966c393bf4693e636fcf12/1b43a/fcp-score.jpg 1892w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-to-improve-fcp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-improve-fcp&quot; aria-label=&quot;how to improve fcp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to Improve FCP&lt;/h2&gt;
&lt;p&gt;As a quick reminder, the first contentful paint is the time from when the page starts loading until the user sees an indication that the page is going to load. Another way of saying that is we need to respond quickly.&lt;/p&gt;
&lt;p&gt;So how do we do that? How do we make sure that we’re responding quickly? Well, let’s conceptually think about our website again.&lt;/p&gt;
&lt;p&gt;All websites inherently have:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d357df6abb6245e43faa172ab2ef18a4/12609/improve-fcp-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHamwFGD//EABgQAQADAQAAAAAAAAAAAAAAAAEAAhEg/9oACAEBAAEFAizNYbx//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAASAy/9oACAEBAAY/ApLf/8QAGRAAAwEBAQAAAAAAAAAAAAAAAAEhERDw/9oACAEBAAE/IWdhcKNeRKmLv//aAAwDAQACAAMAAAAQkA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEBAQEBAQEAAAAAAAAAAAABEQAhQWFR/9oACAEBAAE/EOgglfT9y4BqoAd+sm+LZvloW+6vd//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Website Components&quot;
        title=&quot;Website Components&quot;
        src=&quot;/static/d357df6abb6245e43faa172ab2ef18a4/6a068/improve-fcp-02.jpg&quot;
        srcset=&quot;/static/d357df6abb6245e43faa172ab2ef18a4/09b79/improve-fcp-02.jpg 240w,
/static/d357df6abb6245e43faa172ab2ef18a4/7cc5e/improve-fcp-02.jpg 480w,
/static/d357df6abb6245e43faa172ab2ef18a4/6a068/improve-fcp-02.jpg 960w,
/static/d357df6abb6245e43faa172ab2ef18a4/644c5/improve-fcp-02.jpg 1440w,
/static/d357df6abb6245e43faa172ab2ef18a4/0f98f/improve-fcp-02.jpg 1920w,
/static/d357df6abb6245e43faa172ab2ef18a4/12609/improve-fcp-02.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A set of servers, hosts, or virtual machines.&lt;/li&gt;
&lt;li&gt;A set of documents that need to be delivered across the network to your users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a few different parts of this process that can contribute to slowing down the first contentful paint.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your servers need to be quick.
You need to deliver small documents that can be sent efficiently, and the number of hops through the network needs to be as short as possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;quick-servers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#quick-servers&quot; aria-label=&quot;quick servers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Quick Servers&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9f81f7df1c1e0299668ba6ebaf3ade08/12609/improve-fcp-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe3uiKP/xAAYEAEBAAMAAAAAAAAAAAAAAAABERAhMf/aAAgBAQABBQK7eDTP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAIDH/2gAIAQEABj8Ci//EABoQAQEAAgMAAAAAAAAAAAAAAAERACAxQWH/2gAIAQEAAT8hWcuKWC+ZJUS9On//2gAMAwEAAgADAAAAELAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRABAAMBAQAAAAAAAAAAAAAAAQARITEQ/9oACAEBAAE/EEGoPRYqMKsBsZNRb0Pbdn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Quick Servers&quot;
        title=&quot;Quick Servers&quot;
        src=&quot;/static/9f81f7df1c1e0299668ba6ebaf3ade08/6a068/improve-fcp-03.jpg&quot;
        srcset=&quot;/static/9f81f7df1c1e0299668ba6ebaf3ade08/09b79/improve-fcp-03.jpg 240w,
/static/9f81f7df1c1e0299668ba6ebaf3ade08/7cc5e/improve-fcp-03.jpg 480w,
/static/9f81f7df1c1e0299668ba6ebaf3ade08/6a068/improve-fcp-03.jpg 960w,
/static/9f81f7df1c1e0299668ba6ebaf3ade08/644c5/improve-fcp-03.jpg 1440w,
/static/9f81f7df1c1e0299668ba6ebaf3ade08/0f98f/improve-fcp-03.jpg 1920w,
/static/9f81f7df1c1e0299668ba6ebaf3ade08/12609/improve-fcp-03.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So, how do we make sure your server is quick? We need to ensure that your server is sized correctly for what you’re doing. This is the first step in improving it by focusing on your servers.&lt;/p&gt;
&lt;p&gt;The specific changes to your servers will depend on what you’re doing and what technology stack you’re using. But essentially, you need to focus on three things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sized correctly:&lt;/strong&gt; First, size the hardware, virtual machine, network, or whatever your constraining factors are correctly. Ensure that you have enough overhead in the machines you’ve selected to deliver the content you need.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimal processing:&lt;/strong&gt; Second, minimize the processing needed to fulfill the request. For example, if you just need to serve an index.html page to your users, you probably shouldn’t be making calls to your database unless there’s something especially interesting that you need to do.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network bandwidth:&lt;/strong&gt; Third, the bandwidth to your servers needs to be large enough to fulfill all the requests coming in at once.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;small-documents&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#small-documents&quot; aria-label=&quot;small documents permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Small Documents&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fd280593f5258f0acc1b0ef36339d605/12609/improve-fcp-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7qwA//xAAVEAEBAAAAAAAAAAAAAAAAAAAgQf/aAAgBAQABBQKn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAIDH/2gAIAQEABj8Ci//EABoQAAICAwAAAAAAAAAAAAAAAAEhABEgUWH/2gAIAQEAAT8hfDdwoGBjD//aAAwDAQACAAMAAAAQEA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAEAAgEFAAAAAAAAAAAAAAABABEhEDFBcfD/2gAIAQEAAT8QyorLwRqgVC6OY0VEU2dbc9z/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Small Documents&quot;
        title=&quot;Small Documents&quot;
        src=&quot;/static/fd280593f5258f0acc1b0ef36339d605/6a068/improve-fcp-04.jpg&quot;
        srcset=&quot;/static/fd280593f5258f0acc1b0ef36339d605/09b79/improve-fcp-04.jpg 240w,
/static/fd280593f5258f0acc1b0ef36339d605/7cc5e/improve-fcp-04.jpg 480w,
/static/fd280593f5258f0acc1b0ef36339d605/6a068/improve-fcp-04.jpg 960w,
/static/fd280593f5258f0acc1b0ef36339d605/644c5/improve-fcp-04.jpg 1440w,
/static/fd280593f5258f0acc1b0ef36339d605/0f98f/improve-fcp-04.jpg 1920w,
/static/fd280593f5258f0acc1b0ef36339d605/12609/improve-fcp-04.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There are two major things to consider here: the size of our content and how we compress it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Content size:&lt;/strong&gt; How do you deliver as small a payload as possible while still getting the effectiveness you need? This will depend on your application, but if you’re delivering an HTML page, a JavaScript file, or an image, there are certain upper limits on how much you should send. For an HTML document, if you’re sending anything more than 80 or 100k in total size, that’s too much. An image might be 1mb at its upper limit. If you’re sending larger files, you’re sending too much content to be consumed efficiently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compression:&lt;/strong&gt; Even if you’re sending a 100k HTML document, how you compress that document over the wire can greatly improve speed. Most platforms support Gzip compression, and newer web platforms support more advanced compression such as Brotli. The specific compression method will depend on your technology stack, but compressing your documents can greatly reduce the number of bytes sent over the wire.&lt;/p&gt;
&lt;p&gt;See the following example of how much you can save by compressing your content. I used curl with the —compressed flag to download the HTML of a website. The original size is 700kb, and the compressed size is 200kb.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/aa29469e3e36800203ee0b446c4aa781/5bf79/curl-gzip.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAEDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcqAsB//xAAXEAADAQAAAAAAAAAAAAAAAAABAhAx/9oACAEBAAEFAoKuf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAMBAQAAAAAAAAAAAAAAAAARIQEg/9oACAEBAAE/IWxl5eD/2gAMAwEAAgADAAAAEEPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQAREDEhcZH/2gAIAQEAAT8QWra8loC8aa4c7u5//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Compression&quot;
        title=&quot;Compression&quot;
        src=&quot;/static/aa29469e3e36800203ee0b446c4aa781/6a068/curl-gzip.jpg&quot;
        srcset=&quot;/static/aa29469e3e36800203ee0b446c4aa781/09b79/curl-gzip.jpg 240w,
/static/aa29469e3e36800203ee0b446c4aa781/7cc5e/curl-gzip.jpg 480w,
/static/aa29469e3e36800203ee0b446c4aa781/6a068/curl-gzip.jpg 960w,
/static/aa29469e3e36800203ee0b446c4aa781/644c5/curl-gzip.jpg 1440w,
/static/aa29469e3e36800203ee0b446c4aa781/0f98f/curl-gzip.jpg 1920w,
/static/aa29469e3e36800203ee0b446c4aa781/5bf79/curl-gzip.jpg 3410w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt;  &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; /dev/null &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;File size: %{size_download} bytes&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;&quot;&lt;/span&gt; https://jira.trungk18.com/main.js
File size: &lt;span class=&quot;token number&quot;&gt;756498&lt;/span&gt; bytes

&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Accept-Encoding: gzip&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; /dev/null &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;File size: %{size_download} bytes&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;&quot;&lt;/span&gt; https://jira.trungk18.com/main.js
File size: &lt;span class=&quot;token number&quot;&gt;212983&lt;/span&gt; bytes

&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Accept-Encoding: br&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; /dev/null &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;File size: %{size_download} bytes&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;&quot;&lt;/span&gt; https://jira.trungk18.com/main.js
File size: &lt;span class=&quot;token number&quot;&gt;215579&lt;/span&gt; bytes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;short-transmissions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#short-transmissions&quot; aria-label=&quot;short transmissions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Short Transmissions&lt;/h3&gt;
&lt;p&gt;If we take a look at the network, there’s more than one thing happening. There’s where our servers are in your data center, Amazon, Microsoft, Digital Ocean, or wherever you host your content. And there’s a series of network hops in their infrastructure, which you largely don’t control.&lt;/p&gt;
&lt;p&gt;On the other side, you have your users, connected to their own ISP or wireless network, with a series of hops to manage that network.&lt;/p&gt;
&lt;p&gt;Between them is the infrastructure of the Internet, and this time is something we can control based on where we place our servers and how far they are from our users.&lt;/p&gt;
&lt;p&gt;For example, if your servers are on the East Coast of the United States and your user is on the West Coast, the minimum time is 200 milliseconds due to the speed of light and network hardware. Reducing this distance can greatly improve performance.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b374dc17e491876d064807b7cff0b60/12609/improve-fcp-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAEDBAX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHtVaJAP//EABkQAAIDAQAAAAAAAAAAAAAAAAECABExEP/aAAgBAQABBQJtVngyhfP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAACAwAAAAAAAAAAAAAAAAAAARESIP/aAAgBAQAGPwIdlGf/xAAaEAEAAgMBAAAAAAAAAAAAAAABABEQIUEx/9oACAEBAAE/IVRL2+ThQ7U2EUCmyIVj/9oADAMBAAIAAwAAABDzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAQACAgMBAAAAAAAAAAAAAAEAESExQVFhof/aAAgBAQABPxCleguh7uPubsD49PYlCZTiG9dCmpaY3B3P/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Short Transmissions&quot;
        title=&quot;Short Transmissions&quot;
        src=&quot;/static/4b374dc17e491876d064807b7cff0b60/6a068/improve-fcp-05.jpg&quot;
        srcset=&quot;/static/4b374dc17e491876d064807b7cff0b60/09b79/improve-fcp-05.jpg 240w,
/static/4b374dc17e491876d064807b7cff0b60/7cc5e/improve-fcp-05.jpg 480w,
/static/4b374dc17e491876d064807b7cff0b60/6a068/improve-fcp-05.jpg 960w,
/static/4b374dc17e491876d064807b7cff0b60/644c5/improve-fcp-05.jpg 1440w,
/static/4b374dc17e491876d064807b7cff0b60/0f98f/improve-fcp-05.jpg 1920w,
/static/4b374dc17e491876d064807b7cff0b60/12609/improve-fcp-05.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So how do you reduce this distance? The most effective way is to use CDNs.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/477877b1ce7d0f5fbc2abba4e820975d/12609/improve-fcp-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAIDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdyFoA//xAAaEAABBQEAAAAAAAAAAAAAAAABAAIQETFB/9oACAEBAAEFAjtuQzsf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAACAh/9oACAEBAAY/Air/AP/EABoQAQADAAMAAAAAAAAAAAAAAAEAESEQMVH/2gAIAQEAAT8hh3Uo9mhEKMQ3j//aAAwDAQACAAMAAAAQs8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAZEAEAAwEBAAAAAAAAAAAAAAABABExIUH/2gAIAQEAAT8QrQvrhcDxXguJQnU8ikHTGUlW7B2f/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Short Transmissions&quot;
        title=&quot;Short Transmissions&quot;
        src=&quot;/static/477877b1ce7d0f5fbc2abba4e820975d/6a068/improve-fcp-06.jpg&quot;
        srcset=&quot;/static/477877b1ce7d0f5fbc2abba4e820975d/09b79/improve-fcp-06.jpg 240w,
/static/477877b1ce7d0f5fbc2abba4e820975d/7cc5e/improve-fcp-06.jpg 480w,
/static/477877b1ce7d0f5fbc2abba4e820975d/6a068/improve-fcp-06.jpg 960w,
/static/477877b1ce7d0f5fbc2abba4e820975d/644c5/improve-fcp-06.jpg 1440w,
/static/477877b1ce7d0f5fbc2abba4e820975d/0f98f/improve-fcp-06.jpg 1920w,
/static/477877b1ce7d0f5fbc2abba4e820975d/12609/improve-fcp-06.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;CDNs take the content and place a copy at the edge of each user network, so they can grab it without crossing the Internet. Most CDNs, like CloudFlare and Akamai, do this.&lt;/p&gt;
&lt;p&gt;When a user makes a request, the CDN will pick it up, call your server if they don’t already have a copy, cache the response, and serve it to every user who asks for it.&lt;/p&gt;
&lt;p&gt;Essentially, we’re doing fewer things by not processing each request across the entire network. This is largely about putting infrastructure in place.&lt;/p&gt;
&lt;h2 id=&quot;demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#demo&quot; aria-label=&quot;demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Demo&lt;/h2&gt;
&lt;p&gt;Putting infrastructure in place is very hard to demo and depends on your infrastructure. For example, compression is mostly supported out of the box on most platforms. Some of my web applications are deployed to Netlify and have &lt;code class=&quot;language-text&quot;&gt;Content-Encoding: zstd&lt;/code&gt; in the response headers by default without me doing anything.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a71ba6f4f3102dbf2bf0579d88f3c79a/16845/netlify-compression.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHlwuZg/8QAFRABAQAAAAAAAAAAAAAAAAAAEEH/2gAIAQEAAQUCpT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAEBAQEBAAAAAAAAAAAAAAABABExUf/aAAgBAQABPyF2u7bDvcnpJf/aAAwDAQACAAMAAAAQoM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAACAgIDAAAAAAAAAAAAAAABEQAhMXFRkcH/2gAIAQEAAT8QKi9OGGR7jcmGBYsy9l24or2f/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Netlify Compression&quot;
        title=&quot;Netlify Compression&quot;
        src=&quot;/static/a71ba6f4f3102dbf2bf0579d88f3c79a/6a068/netlify-compression.jpg&quot;
        srcset=&quot;/static/a71ba6f4f3102dbf2bf0579d88f3c79a/09b79/netlify-compression.jpg 240w,
/static/a71ba6f4f3102dbf2bf0579d88f3c79a/7cc5e/netlify-compression.jpg 480w,
/static/a71ba6f4f3102dbf2bf0579d88f3c79a/6a068/netlify-compression.jpg 960w,
/static/a71ba6f4f3102dbf2bf0579d88f3c79a/644c5/netlify-compression.jpg 1440w,
/static/a71ba6f4f3102dbf2bf0579d88f3c79a/0f98f/netlify-compression.jpg 1920w,
/static/a71ba6f4f3102dbf2bf0579d88f3c79a/16845/netlify-compression.jpg 2406w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;To improve First Contentful Paint (FCP), you need to focus on the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quick servers: Ensure your servers are sized correctly, minimize processing, and have enough network bandwidth.&lt;/li&gt;
&lt;li&gt;Small documents: Deliver small payloads and compress them effectively.&lt;/li&gt;
&lt;li&gt;Short transmissions: Use CDNs to reduce the distance between your servers and your users.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/courses/web-perf&quot;&gt;Web Performance Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Measuring Web Performance]]></title><description><![CDATA[Learn how to measure web performance and Core Web Vitals using tools like Lighthouse, WebPageTest, and Chrome DevTools.]]></description><link>https://trungvose.commeasuring-web-performance/</link><guid isPermaLink="false">https://trungvose.commeasuring-web-performance/</guid><pubDate>Sat, 15 Jul 2023 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Understanding how to measure web performance is crucial for ensuring a fast and efficient user experience. In this guide, we’ll explore various tools like Lighthouse, WebPageTest, and Chrome DevTools to help you measure web performance effectively.&lt;/p&gt;
&lt;p&gt;Before diving into these tools, it’s important to understand the types of performance data you can collect. There are two main types: lab data and field data.&lt;/p&gt;
&lt;h2 id=&quot;lab-data-and-field-data&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lab-data-and-field-data&quot; aria-label=&quot;lab data and field data permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lab Data and Field Data&lt;/h2&gt;
&lt;h3 id=&quot;lab-data&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lab-data&quot; aria-label=&quot;lab data permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lab Data&lt;/h3&gt;
&lt;p&gt;Lab data describes how hypothetical users may experience your website. This data is collected through controlled tests, such as those performed by Lighthouse. It represents a single webpage load from a specific location on the network.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3002759ec50369ce2cc1547b10b29c2f/12609/measure-performance-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHWiWBQ0P/EABoQAAIDAQEAAAAAAAAAAAAAAAEDAAIREyH/2gAIAQEAAQUCB96DFMqyjyRM2CoE/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGxABAQABBQAAAAAAAAAAAAAAAQACERIhMUL/2gAIAQEABj8C9SogW7GNLu4C/8QAGhABAQEAAwEAAAAAAAAAAAAAEQEAITFhQf/aAAgBAQABPyEQVw4J8G1zYgnenOc634X4aERTyb//2gAMAwEAAgADAAAAEFjv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qp//EABwQAQADAAMBAQAAAAAAAAAAAAEAESFBcYExUf/aAAgBAQABPxAMTWHKa47lgvmqgP2OkA19UnfkAx0VR49IS3i1BJrnroQn/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Lab Data&quot;
        title=&quot;Lab Data&quot;
        src=&quot;/static/3002759ec50369ce2cc1547b10b29c2f/6a068/measure-performance-01.jpg&quot;
        srcset=&quot;/static/3002759ec50369ce2cc1547b10b29c2f/09b79/measure-performance-01.jpg 240w,
/static/3002759ec50369ce2cc1547b10b29c2f/7cc5e/measure-performance-01.jpg 480w,
/static/3002759ec50369ce2cc1547b10b29c2f/6a068/measure-performance-01.jpg 960w,
/static/3002759ec50369ce2cc1547b10b29c2f/644c5/measure-performance-01.jpg 1440w,
/static/3002759ec50369ce2cc1547b10b29c2f/0f98f/measure-performance-01.jpg 1920w,
/static/3002759ec50369ce2cc1547b10b29c2f/12609/measure-performance-01.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Lab data is often referred to as “Synthetic Testing” because it measures performance from a known device and network. It provides an estimate of performance rather than actual user experience.&lt;/p&gt;
&lt;h3 id=&quot;field-data&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#field-data&quot; aria-label=&quot;field data permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Field Data&lt;/h3&gt;
&lt;p&gt;Field data describes how real users actually experienced your website. Field data is also known as Real User Monitoring (RUM), and is typically collected by monitoring real user experiences using JavaScript on the pages they load, and reporting various metrics to an analytics solution&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/60fa46fcc81ebf64eb0a8c860fe37377/12609/measure-performance-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBAgQF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB61NKyBgf/8QAGxAAAgMAAwAAAAAAAAAAAAAAAQIAAxESEyH/2gAIAQEAAQUC33tUSqxbEM4bFUAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAgEFAQAAAAAAAAAAAAAAAAECEBESMUKR/9oACAEBAAY/AuhuSsZRrpeH/8QAGxABAQEAAgMAAAAAAAAAAAAAAREAITFBYYH/2gAIAQEAAT8hOiPi5AgQvLrYJZ3isjM+ZOPWij4Z/9oADAMBAAIAAwAAABBoL//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAwEBPxARf//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxA1f//EAB0QAAICAgMBAAAAAAAAAAAAAAERACExQVFxgbH/2gAIAQEAAT8QWAAs1IrXcK0sR4c0/kOiAGWSI78g1m+txwGiI90d8RMkN0Cf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Field Data&quot;
        title=&quot;Field Data&quot;
        src=&quot;/static/60fa46fcc81ebf64eb0a8c860fe37377/6a068/measure-performance-02.jpg&quot;
        srcset=&quot;/static/60fa46fcc81ebf64eb0a8c860fe37377/09b79/measure-performance-02.jpg 240w,
/static/60fa46fcc81ebf64eb0a8c860fe37377/7cc5e/measure-performance-02.jpg 480w,
/static/60fa46fcc81ebf64eb0a8c860fe37377/6a068/measure-performance-02.jpg 960w,
/static/60fa46fcc81ebf64eb0a8c860fe37377/644c5/measure-performance-02.jpg 1440w,
/static/60fa46fcc81ebf64eb0a8c860fe37377/0f98f/measure-performance-02.jpg 1920w,
/static/60fa46fcc81ebf64eb0a8c860fe37377/12609/measure-performance-02.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Field data can be extensive and sometimes noisy, meaning it includes irrelevant information. To make sense of field data, you need to use statistical methods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interpreting Performance Data with Statistics&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Averages are a simple way to understand data sets, but they can be misleading due to uneven performance distribution. For example, an average Lighthouse score of 80 could result from very different performance scenarios.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/12609/measure-performance-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAIDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdsiWg//xAAbEAACAQUAAAAAAAAAAAAAAAAAARACAxESIf/aAAgBAQABBQLUXYdI7eY//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRAAAgMBAAAAAAAAAAAAAAAAARAAITEi/9oACAEBAAY/AtLN7D0bX//EABwQAAICAgMAAAAAAAAAAAAAAAERADEQcSFBsf/aAAgBAQABPyEKbootEbw28eJ0IgGoOAp//9oADAMBAAIAAwAAABBDL//EABYRAQEBAAAAAAAAAAAAAAAAAAEQQf/aAAgBAwEBPxADZ//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxClf//EAB0QAQEAAgIDAQAAAAAAAAAAAAERACExURBBcdH/2gAIAQEAAT8QkKDpfzNK2kiTwmrSSTh1jNk8ROPs+4IUsJXP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Performance Data&quot;
        title=&quot;Performance Data&quot;
        src=&quot;/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/6a068/measure-performance-03.jpg&quot;
        srcset=&quot;/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/09b79/measure-performance-03.jpg 240w,
/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/7cc5e/measure-performance-03.jpg 480w,
/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/6a068/measure-performance-03.jpg 960w,
/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/644c5/measure-performance-03.jpg 1440w,
/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/0f98f/measure-performance-03.jpg 1920w,
/static/c7cd007c6e200e8ce1f1f5a09ad9a4fc/12609/measure-performance-03.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To get a clearer picture, use median and percentiles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Median and Percentiles&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The median score is the middle value in a sorted list of performance scores, representing the typical user experience. Percentiles like p50, p75, and p95 show the performance for different segments of users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;p50 (50th percentile)&lt;/strong&gt;: This is the median score, indicating that 50% of users have a performance score below this value and 50% have a score above it. It represents the “typical user” experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;p75 (75th percentile)&lt;/strong&gt;: This score indicates that 75% of users have a performance score below this value and 25% have a score above it. It represents the experience of “most users.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;p95 (95th percentile)&lt;/strong&gt;: This score indicates that 95% of users have a performance score below this value and only 5% have a score above it. It represents the experience of the “worst users.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/748d29792cde41496b0492bc3c71c322/12609/measure-performance-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdh2FiD/xAAYEAADAQEAAAAAAAAAAAAAAAAAAREQAv/aAAgBAQABBQJqEx80mf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABgQAAMBAQAAAAAAAAAAAAAAAAABMRBB/9oACAEBAAY/Ausrylz/xAAaEAACAwEBAAAAAAAAAAAAAAABEQAQITFh/9oACAEBAAE/ISqjLL1VELuPvhV//9oADAMBAAIAAwAAABAQ7//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxClf//EABwQAQEAAgIDAAAAAAAAAAAAAAERACEQUXGRof/aAAgBAQABPxAraWMQnzFbNY6DfjgIpg0AMWBdRUPeAhFr3M//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Performance Data&quot;
        title=&quot;Performance Data&quot;
        src=&quot;/static/748d29792cde41496b0492bc3c71c322/6a068/measure-performance-04.jpg&quot;
        srcset=&quot;/static/748d29792cde41496b0492bc3c71c322/09b79/measure-performance-04.jpg 240w,
/static/748d29792cde41496b0492bc3c71c322/7cc5e/measure-performance-04.jpg 480w,
/static/748d29792cde41496b0492bc3c71c322/6a068/measure-performance-04.jpg 960w,
/static/748d29792cde41496b0492bc3c71c322/644c5/measure-performance-04.jpg 1440w,
/static/748d29792cde41496b0492bc3c71c322/0f98f/measure-performance-04.jpg 1920w,
/static/748d29792cde41496b0492bc3c71c322/12609/measure-performance-04.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here’s a simple code example to calculate percentiles:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Performance scores, sorted.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; lighthouseScores &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Desired percentile to calculate.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; percentile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.75&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Find the index 75% into the array.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; idx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lighthouseScores&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; percentile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; p75Score &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lighthouseScores&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;idx&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;tools-to-measure-web-performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tools-to-measure-web-performance&quot; aria-label=&quot;tools to measure web performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tools to Measure Web Performance&lt;/h2&gt;
&lt;p&gt;Now that you understand the metrics and methods for measuring web performance, let’s look at the tools available to help you&lt;/p&gt;
&lt;p&gt;Refer to &lt;a href=&quot;https://web.dev/articles/vitals-tools&quot;&gt;Core Web Vitals workflows with Google tools&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;1-lighthouse--pagespeed-insights&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-lighthouse--pagespeed-insights&quot; aria-label=&quot;1 lighthouse  pagespeed insights permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Lighthouse &amp;#x26; PageSpeed Insights&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Type: Synthetic Lab Data&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot;&gt;Lighthouse&lt;/a&gt; is an open-source tool from Google that can be run from Chrome DevTools or from the command line.&lt;/p&gt;
&lt;p&gt;Lighthouse runs from you local computer, so it’s measuring the performance you experience with your hardware on your network.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot;&gt;Lighthouse-CI&lt;/a&gt; is a related tool that runs Lighthouse during project builds and deploys to assist with performance regression testing. It presents a Lighthouse report along with pull requests, and tracks performance metrics over time.&lt;/p&gt;
&lt;p&gt;It generates an overall performance “score” to make you feel good (or bad) about your site’s speed. This score can be useful, but has some limitations.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/228a05380081f8e448dbe6ab0f4fe878/cecd0/measure-performance-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABy4USD//EABYQAAMAAAAAAAAAAAAAAAAAAAISIP/aAAgBAQABBQJyr//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABcQAAMBAAAAAAAAAAAAAAAAAAEQIDH/2gAIAQEABj8C2Av/xAAcEAEAAgEFAAAAAAAAAAAAAAABADEQESFBYXH/2gAIAQEAAT8hEtTi2a9sLIm3jH//2gAMAwEAAgADAAAAEOAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAAMAAwAAAAAAAAAAAAAAAQARMSGh4f/aAAgBAQABPxCitXthLUaQXpOwQMNCx4Wp/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Lighthouse&quot;
        title=&quot;Lighthouse&quot;
        src=&quot;/static/228a05380081f8e448dbe6ab0f4fe878/6a068/measure-performance-05.jpg&quot;
        srcset=&quot;/static/228a05380081f8e448dbe6ab0f4fe878/09b79/measure-performance-05.jpg 240w,
/static/228a05380081f8e448dbe6ab0f4fe878/7cc5e/measure-performance-05.jpg 480w,
/static/228a05380081f8e448dbe6ab0f4fe878/6a068/measure-performance-05.jpg 960w,
/static/228a05380081f8e448dbe6ab0f4fe878/644c5/measure-performance-05.jpg 1440w,
/static/228a05380081f8e448dbe6ab0f4fe878/0f98f/measure-performance-05.jpg 1920w,
/static/228a05380081f8e448dbe6ab0f4fe878/cecd0/measure-performance-05.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There are several places where you can run Lighthouse-as-a-Service from elsewhere on the internet, including Google’s &lt;a href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ad14ef89a1138422fcecde7954202b0d/5f17e/measure-performance-10.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.16666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAASABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3rKAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAEFAh//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAY/Ah//xAAZEAADAAMAAAAAAAAAAAAAAAABEBEAMUH/2gAIAQEAAT8hQEG7hfX/AP/aAAwDAQACAAMAAAAQoMc8/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPxAf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPxAf/8QAHBABAAICAwEAAAAAAAAAAAAAAQARITEQQXFR/9oACAEBAAE/EEpxdewerlQKX12xNgb3HKA547+c/wD/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PageSpeed Insights&quot;
        title=&quot;PageSpeed Insights&quot;
        src=&quot;/static/ad14ef89a1138422fcecde7954202b0d/6a068/measure-performance-10.jpg&quot;
        srcset=&quot;/static/ad14ef89a1138422fcecde7954202b0d/09b79/measure-performance-10.jpg 240w,
/static/ad14ef89a1138422fcecde7954202b0d/7cc5e/measure-performance-10.jpg 480w,
/static/ad14ef89a1138422fcecde7954202b0d/6a068/measure-performance-10.jpg 960w,
/static/ad14ef89a1138422fcecde7954202b0d/644c5/measure-performance-10.jpg 1440w,
/static/ad14ef89a1138422fcecde7954202b0d/0f98f/measure-performance-10.jpg 1920w,
/static/ad14ef89a1138422fcecde7954202b0d/5f17e/measure-performance-10.jpg 2532w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-chrome-user-experience-report-crux&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-chrome-user-experience-report-crux&quot; aria-label=&quot;2 chrome user experience report crux permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Chrome User Experience Report (CrUX)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Type: Field Data&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Chrome browser itself collects performance metrics from opt-in users for the top million domains on the internet. Google publishes these metrics in the &lt;a href=&quot;https://developer.chrome.com/docs/crux/dashboard&quot;&gt;Chrome User Experience Report or CrUX&lt;/a&gt;. It’s real-user data!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fb9b7374a295c707f5d2b98e273d627a/4c6a6/measure-performance-08.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEDAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHsquRFQ//EABgQAAMBAQAAAAAAAAAAAAAAAAACEhBB/9oACAEBAAEFAoQhSEOZ/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGBAAAwEBAAAAAAAAAAAAAAAAATKRACD/2gAIAQEABj8CQTIJkE4//8QAGhABAAEFAAAAAAAAAAAAAAAAAPEBECGBwf/aAAgBAQABPyHFxIUiVm1H/9oADAMBAAIAAwAAABBTD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAQABBAMAAAAAAAAAAAAAAAEAESExQWFx0f/aAAgBAQABPxDLu8eUDxGKEprtBQ06gQu1n//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Chrome User Experience Report (CrUX)&quot;
        title=&quot;Chrome User Experience Report (CrUX)&quot;
        src=&quot;/static/fb9b7374a295c707f5d2b98e273d627a/6a068/measure-performance-08.jpg&quot;
        srcset=&quot;/static/fb9b7374a295c707f5d2b98e273d627a/09b79/measure-performance-08.jpg 240w,
/static/fb9b7374a295c707f5d2b98e273d627a/7cc5e/measure-performance-08.jpg 480w,
/static/fb9b7374a295c707f5d2b98e273d627a/6a068/measure-performance-08.jpg 960w,
/static/fb9b7374a295c707f5d2b98e273d627a/644c5/measure-performance-08.jpg 1440w,
/static/fb9b7374a295c707f5d2b98e273d627a/0f98f/measure-performance-08.jpg 1920w,
/static/fb9b7374a295c707f5d2b98e273d627a/4c6a6/measure-performance-08.jpg 2526w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5a42f4d4eea653b9869dc4d39056abd6/6eed6/measure-performance-09.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 113.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAXABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB7sWyk1QqaBcH/8QAGxAAAgIDAQAAAAAAAAAAAAAAAAECMRAREiH/2gAIAQEAAQUCOkL3DT3GhxFX/8QAFREBAQAAAAAAAAAAAAAAAAAAASD/2gAIAQMBAT8BY//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABkQAAIDAQAAAAAAAAAAAAAAAAEQAAIxMv/aAAgBAQAGPwKaHlX0V//EABwQAQEBAAIDAQAAAAAAAAAAAAERACFhMUFxkf/aAAgBAQABPyH35yLG33IFG7m+M1SHeCcgHrfuRbHocZFX7v/aAAwDAQACAAMAAAAQu9BB/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAxEP/aAAgBAwEBPxBJkSDvP//EABYRAQEBAAAAAAAAAAAAAAAAABARAf/aAAgBAgEBPxDSU//EABwQAQACAwADAAAAAAAAAAAAAAEAESFBUTFh0f/aAAgBAQABPxAdb+sfI6EGnIgkQ6NkHBidi07RDcAnIeMCm7hmQOhREJINrM//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Chrome User Experience Report (CrUX)&quot;
        title=&quot;Chrome User Experience Report (CrUX)&quot;
        src=&quot;/static/5a42f4d4eea653b9869dc4d39056abd6/6a068/measure-performance-09.jpg&quot;
        srcset=&quot;/static/5a42f4d4eea653b9869dc4d39056abd6/09b79/measure-performance-09.jpg 240w,
/static/5a42f4d4eea653b9869dc4d39056abd6/7cc5e/measure-performance-09.jpg 480w,
/static/5a42f4d4eea653b9869dc4d39056abd6/6a068/measure-performance-09.jpg 960w,
/static/5a42f4d4eea653b9869dc4d39056abd6/644c5/measure-performance-09.jpg 1440w,
/static/5a42f4d4eea653b9869dc4d39056abd6/6eed6/measure-performance-09.jpg 1790w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CrUX only represents Chrome users, and even then, only a subset of Chrome users&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-google-search-console&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-google-search-console&quot; aria-label=&quot;3 google search console permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Google Search Console&lt;/h3&gt;
&lt;p&gt;Google Search Console shows the analytics, issues, and performance recorded by the Googlebot crawler when Google indexes your website. This includes User Experience metrics like the Core Web Vitals.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/32614df73ff4854bf3ab784999b1ce74/002df/measure-performance-07.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHYS4BiRf/EABkQAAIDAQAAAAAAAAAAAAAAAAECEBESIf/aAAgBAQABBQJrCh+1GFj/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAbEAACAQUAAAAAAAAAAAAAAAABERAAMkJxgf/aAAgBAQAGPwJhdrFbm0R//8QAGhABAQEAAwEAAAAAAAAAAAAAAREAEFFh8f/aAAgBAQABPyFwFepmSXuTJwCRCb4vH//aAAwDAQACAAMAAAAQv8//xAAWEQEBAQAAAAAAAAAAAAAAAAARARD/2gAIAQMBAT8QqEz/xAAWEQEBAQAAAAAAAAAAAAAAAAABERD/2gAIAQIBAT8QRtHP/8QAGxABAAICAwAAAAAAAAAAAAAAAQAhETFBcfH/2gAIAQEAAT8QVqBugHcVlfmSKWx0wtiZGeFmZ//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Google Search Console&quot;
        title=&quot;Google Search Console&quot;
        src=&quot;/static/32614df73ff4854bf3ab784999b1ce74/6a068/measure-performance-07.jpg&quot;
        srcset=&quot;/static/32614df73ff4854bf3ab784999b1ce74/09b79/measure-performance-07.jpg 240w,
/static/32614df73ff4854bf3ab784999b1ce74/7cc5e/measure-performance-07.jpg 480w,
/static/32614df73ff4854bf3ab784999b1ce74/6a068/measure-performance-07.jpg 960w,
/static/32614df73ff4854bf3ab784999b1ce74/644c5/measure-performance-07.jpg 1440w,
/static/32614df73ff4854bf3ab784999b1ce74/0f98f/measure-performance-07.jpg 1920w,
/static/32614df73ff4854bf3ab784999b1ce74/002df/measure-performance-07.jpg 2546w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Although the Search Console metrics are synthetic, they are what Google will use to rank your site in search results. They represent a very important user: &lt;em&gt;Google&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The metrics you’ll see in Search Console will be quite slow to update, depending on the traffic to your website. It could be a week or more for Google to see changes in your performance scores, and the reports are very generic.&lt;/p&gt;
&lt;p&gt;You need to use Search Console to see how Google ranks your performance, but it’s not very useful for testing or discovering performance issues.&lt;/p&gt;
&lt;h3 id=&quot;4-chrome-devtools-update-september-2024&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-chrome-devtools-update-september-2024&quot; aria-label=&quot;4 chrome devtools update september 2024 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Chrome DevTools (Update September 2024)&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Type: Synthetic Lab Data (with some context from CrUX)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chrome has so many amazing updates since the last time I wrote this article. The Chrome DevTools now has a new Performance panel that shows you the Core Web Vitals for your website.&lt;/p&gt;
&lt;p&gt;When you open the Performance panel, it immediately captures and shows you your local &lt;a href=&quot;https://web.dev/articles/lcp&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/articles/cls&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; metrics tells you their score (good, needs improvement, or bad).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/191a6988c6278fe4185e58668c0b69a0/84b0d/measure-performance-11.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAYCBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe3JV8cGB//EABoQAAEFAQAAAAAAAAAAAAAAAAIAEBESMTL/2gAIAQEAAQUCqMlrF0v/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAACAwEAAAAAAAAAAAAAAAABEAAxcZH/2gAIAQEABj8CochZ1f/EABoQAAICAwAAAAAAAAAAAAAAAAABEHERMaH/2gAIAQEAAT8hBmxcx2LZ1I//2gAMAwEAAgADAAAAEDPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBAAAgICAwAAAAAAAAAAAAAAAAERITFRseHw/9oACAEBAAE/EOtBScToeLGuhynqbJez/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Chrome DevTools&quot;
        title=&quot;Chrome DevTools&quot;
        src=&quot;/static/191a6988c6278fe4185e58668c0b69a0/6a068/measure-performance-11.jpg&quot;
        srcset=&quot;/static/191a6988c6278fe4185e58668c0b69a0/09b79/measure-performance-11.jpg 240w,
/static/191a6988c6278fe4185e58668c0b69a0/7cc5e/measure-performance-11.jpg 480w,
/static/191a6988c6278fe4185e58668c0b69a0/6a068/measure-performance-11.jpg 960w,
/static/191a6988c6278fe4185e58668c0b69a0/644c5/measure-performance-11.jpg 1440w,
/static/191a6988c6278fe4185e58668c0b69a0/0f98f/measure-performance-11.jpg 1920w,
/static/191a6988c6278fe4185e58668c0b69a0/84b0d/measure-performance-11.jpg 5116w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The new Performance panel is pretty cool, thus I decided to write a new article &lt;a href=&quot;/devtools-performance-panel&quot;&gt;Performance panel: Analyze your website’s performance&lt;/a&gt; about it. Stay tuned!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Performance panel is a developer tool that primarily provides lab data—though with some context from CrUX. It’s not a substitute for field data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;5-webpagetest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-webpagetest&quot; aria-label=&quot;5 webpagetest permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. WebPageTest&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Type: Synthetic Lab Data&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.webpagetest.org/&quot;&gt;WebPageTest&lt;/a&gt; is a free hosted service that performs performance tests on public websites. It can do a lot more things than Lighthouse, like setting up network locations, network speeds, and customizing requests.&lt;/p&gt;
&lt;p&gt;It also produces a more detailed (and more complex) report with network location, breakdown of timings, and a detailed waterfall chart.&lt;/p&gt;
&lt;p&gt;WebPageTest is great for auditing live websites to better understand how they are performing in production.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/355b5982a3323b33ab4dd3df9e0a1adb/06073/measure-performance-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 78.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAABAv/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAFHT9QM6P/EABoQAAIDAQEAAAAAAAAAAAAAAAACAQMEEhT/2gAIAQEAAQUC8KCYkRrMaWP1J0RJ/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGxAAAgIDAQAAAAAAAAAAAAAAAAECERIxMnH/2gAIAQEABj8C6kKWTdGVteGmcs0f/8QAHBABAAIBBQAAAAAAAAAAAAAAAQARQSGRweHx/9oACAEBAAE/IfGIFFK6Y6bnFKmpxS/REc9p/9oADAMBAAIAAwAAABDv/wD/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxBH/8QAFhEAAwAAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EKys/8QAHRABAAICAwEBAAAAAAAAAAAAAQARIVFBYfBx4f/aAAgBAQABPxC4R9HUI7IEU9OIHtQSAorUrWXOm4cvl9gCoq8/qf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;WebPageTest&quot;
        title=&quot;WebPageTest&quot;
        src=&quot;/static/355b5982a3323b33ab4dd3df9e0a1adb/6a068/measure-performance-06.jpg&quot;
        srcset=&quot;/static/355b5982a3323b33ab4dd3df9e0a1adb/09b79/measure-performance-06.jpg 240w,
/static/355b5982a3323b33ab4dd3df9e0a1adb/7cc5e/measure-performance-06.jpg 480w,
/static/355b5982a3323b33ab4dd3df9e0a1adb/6a068/measure-performance-06.jpg 960w,
/static/355b5982a3323b33ab4dd3df9e0a1adb/644c5/measure-performance-06.jpg 1440w,
/static/355b5982a3323b33ab4dd3df9e0a1adb/0f98f/measure-performance-06.jpg 1920w,
/static/355b5982a3323b33ab4dd3df9e0a1adb/06073/measure-performance-06.jpg 3036w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;Measuring web performance is essential for ensuring a fast and efficient user experience. By using tools like Lighthouse, WebPageTest, and Chrome DevTools, you can collect lab and field data to evaluate your website’s performance effectively.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/courses/web-perf&quot;&gt;Web Performance Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/vitals-tools&quot;&gt;Core Web Vitals Tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Angular augmenting native elements]]></title><link>https://trungvose.comangular-augmenting-native-element/</link><guid isPermaLink="false">https://trungvose.comangular-augmenting-native-element/</guid><pubDate>Mon, 19 Jun 2023 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We build new components every day in our Angular application. A few core principles, such as augmenting native elements or utilising the directive selector, could help develop fantastic components. In this post, we’ll go back to the basics to revisit the power of Angular directive selectors in ways that simplify the component’s implementation and improve accessibility.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Components are the most basic UI building block of an Angular app.
An Angular app contains a tree of Angular components.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We will look into a rather simple use case and see how &lt;code class=&quot;language-text&quot;&gt;augmenting native elements&lt;/code&gt; could help.&lt;/p&gt;
&lt;h2 id=&quot;source-code-and-slide&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-slide&quot; aria-label=&quot;source code and slide permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and slide&lt;/h2&gt;
&lt;p&gt;This blog post is based on my recent presentation at NGRome 2022.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View the original slide → &lt;a href=&quot;https://trungvose.com/ngrome22&quot;&gt;trungvose.com/ngrome22&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;View the demo → &lt;a href=&quot;https://stackblitz.com/edit/angular-directives-use-case?file=src/app/button-v2/button-v2.component.ts&quot;&gt;https://stackblitz.com/edit/angular-directives-use-case&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;creating-a-custom-button-component&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#creating-a-custom-button-component&quot; aria-label=&quot;creating a custom button component permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Creating a custom button component&lt;/h2&gt;
&lt;p&gt;We will create a custom button component with three variants&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A button with simply a text&lt;/li&gt;
&lt;li&gt;A button with text and an icon&lt;/li&gt;
&lt;li&gt;A button with text and an icon, behave like a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;a&gt;&lt;/code&gt; tag and open a new URL when clicked.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 510px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/46b2d19fe34a3c87f2b31b565a9116f8/18815/05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAARABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAMEBQH/xAAVAQEBAAAAAAAAAAAAAAAAAAACAf/aAAwDAQACEAMQAAAB3OZ81NoRVZBGYFf/xAAaEAACAgMAAAAAAAAAAAAAAAAAAgMQExQx/9oACAEBAAEFAr2SKbI1J0//xAAVEQEBAAAAAAAAAAAAAAAAAAARIP/aAAgBAwEBPwFj/8QAFhEAAwAAAAAAAAAAAAAAAAAAAREg/9oACAECAQE/AUY//8QAGxAAAgEFAAAAAAAAAAAAAAAAAREAEBIggZL/2gAIAQEABj8Cq7R1CEt4/wD/xAAdEAABAwUBAAAAAAAAAAAAAAABEBFBACExUWFx/9oACAEBAAE/IbvzxQJ2qQ7B5LkT/9oADAMBAAIAAwAAABBfwHz/xAAXEQEBAQEAAAAAAAAAAAAAAAABECEx/9oACAEDAQE/EAuQ7P/EABcRAQEBAQAAAAAAAAAAAAAAAAEQETH/2gAIAQIBAT8QSNjyf//EAB0QAAEEAgMAAAAAAAAAAAAAAAEQETFBACGBkaH/2gAIAQEAAT8Qakcz5Eow7wWIPWAYJbwAJal8if/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular augmenting native elements&quot;
        title=&quot;Angular augmenting native elements&quot;
        src=&quot;/static/46b2d19fe34a3c87f2b31b565a9116f8/18815/05.jpg&quot;
        srcset=&quot;/static/46b2d19fe34a3c87f2b31b565a9116f8/09b79/05.jpg 240w,
/static/46b2d19fe34a3c87f2b31b565a9116f8/7cc5e/05.jpg 480w,
/static/46b2d19fe34a3c87f2b31b565a9116f8/18815/05.jpg 510w&quot;
        sizes=&quot;(max-width: 510px) 100vw, 510px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackblitz.com/edit/angular-directives-use-case&quot;&gt;stackblitz.com/edit/angular-directives-use-case&lt;/a&gt; ↗&lt;/p&gt;
&lt;h3 id=&quot;custom-button-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-button-1&quot; aria-label=&quot;custom button 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom button #1&lt;/h3&gt;
&lt;p&gt;That’s the simple implementation for the first requirement that simple can render a button with specific styling and text content.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;reset&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;submit&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonTheme&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;primary&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;secondary&apos;&lt;/span&gt;

@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;shared-button&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    &amp;lt;button
      class=&quot;button&quot;
      [ngClass]=&quot;&apos;btn-&apos; + buttonTheme&quot;
      [attr.type]=&quot;buttonType&quot;
    &gt;
      &amp;lt;span class=&quot;button-text&quot;&gt;{{ buttonText }}&amp;lt;/span&gt;
    &amp;lt;/button&gt;
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  changeDetection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ChangeDetectionStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OnPush&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  @&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonText&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  @&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonTheme&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;secondary&apos;&lt;/span&gt;
  @&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To create a new component in Angular, we usually need at least three parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A selector, in this case, is &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The template to render when we use our component&lt;/li&gt;
&lt;li&gt;A class that defines the behaviour of our component. In the use case of a button, there isn’t any behaviour needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s how we use it to render the first use-case, notice that you only need to give the &lt;code class=&quot;language-text&quot;&gt;[buttonText]&lt;/code&gt; input and we will have the text &lt;code class=&quot;language-text&quot;&gt;Login&lt;/code&gt; render inside the button for us.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/98f329eefeea8edd544cf65778b4033c/12609/Button_1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAUC/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAABoZogD//EABoQAAICAwAAAAAAAAAAAAAAAAEEABICEDH/2gAIAQEAAQUCzLFrNQc3/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHRAAAQIHAAAAAAAAAAAAAAAAAgABERIgITNxkf/aAAgBAQAGPwJ5QGG1jDqvR//EABkQAAIDAQAAAAAAAAAAAAAAAAERABBBIf/aAAgBAQABPyEN0fhNMMmtsT//2gAMAwEAAgADAAAAELgP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAAEEAwAAAAAAAAAAAAAAAQAQESFBUWHw/9oACAEBAAE/EBkIsQprcxYXzuJIAtLDmqWf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Button #1.jpg&quot;
        title=&quot;Button #1.jpg&quot;
        src=&quot;/static/98f329eefeea8edd544cf65778b4033c/6a068/Button_1.jpg&quot;
        srcset=&quot;/static/98f329eefeea8edd544cf65778b4033c/09b79/Button_1.jpg 240w,
/static/98f329eefeea8edd544cf65778b4033c/7cc5e/Button_1.jpg 480w,
/static/98f329eefeea8edd544cf65778b4033c/6a068/Button_1.jpg 960w,
/static/98f329eefeea8edd544cf65778b4033c/644c5/Button_1.jpg 1440w,
/static/98f329eefeea8edd544cf65778b4033c/0f98f/Button_1.jpg 1920w,
/static/98f329eefeea8edd544cf65778b4033c/12609/Button_1.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Below is how it gets rendered into the DOM. As we can see there is a &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt; that wraps the real native &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6f0fc1e0adbd29a2ab9c00570aaace23/d8536/01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 26.666666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcqokAf/xAAWEAADAAAAAAAAAAAAAAAAAAAQEUH/2gAIAQEAAQUCjH//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAWEAADAAAAAAAAAAAAAAAAAAABEDH/2gAIAQEAAT8hgN//2gAMAwEAAgADAAAAEPfv/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QJ//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGf/xAAaEAEAAgMBAAAAAAAAAAAAAAABABEhQWGR/9oACAEBAAE/EFUXqNsJct55P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular augmenting native elements&quot;
        title=&quot;Angular augmenting native elements&quot;
        src=&quot;/static/6f0fc1e0adbd29a2ab9c00570aaace23/6a068/01.jpg&quot;
        srcset=&quot;/static/6f0fc1e0adbd29a2ab9c00570aaace23/09b79/01.jpg 240w,
/static/6f0fc1e0adbd29a2ab9c00570aaace23/7cc5e/01.jpg 480w,
/static/6f0fc1e0adbd29a2ab9c00570aaace23/6a068/01.jpg 960w,
/static/6f0fc1e0adbd29a2ab9c00570aaace23/644c5/01.jpg 1440w,
/static/6f0fc1e0adbd29a2ab9c00570aaace23/d8536/01.jpg 1862w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;custom-button-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-button-2&quot; aria-label=&quot;custom button 2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom button #2&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca17da8c3e8375a2920a892b17c21888/12609/Button_2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAQAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB35BiP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABgQAAIDAAAAAAAAAAAAAAAAAAABESBx/9oACAEBAAE/IY0VEf/aAAwDAQACAAMAAAAQEA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAQUBAAAAAAAAAAAAAAABABEgMUFxkf/aAAgBAQABPxBq36iUwnbEs//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Button #2.jpg&quot;
        title=&quot;Button #2.jpg&quot;
        src=&quot;/static/ca17da8c3e8375a2920a892b17c21888/6a068/Button_2.jpg&quot;
        srcset=&quot;/static/ca17da8c3e8375a2920a892b17c21888/09b79/Button_2.jpg 240w,
/static/ca17da8c3e8375a2920a892b17c21888/7cc5e/Button_2.jpg 480w,
/static/ca17da8c3e8375a2920a892b17c21888/6a068/Button_2.jpg 960w,
/static/ca17da8c3e8375a2920a892b17c21888/644c5/Button_2.jpg 1440w,
/static/ca17da8c3e8375a2920a892b17c21888/0f98f/Button_2.jpg 1920w,
/static/ca17da8c3e8375a2920a892b17c21888/12609/Button_2.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the second example, we want to render the text along with an icon. The simplest way to do it is introducing new &lt;code class=&quot;language-text&quot;&gt;@Input() icon: string&lt;/code&gt; and our &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt; will use this icon to render with our favourite icon library, e.g. material icon. The code could potentially look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;export type ButtonType = &apos;reset&apos; | &apos;button&apos; | &apos;submit&apos;;
export type ButtonTheme = &apos;primary&apos; | &apos;secondary&apos;;

@Component({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;selector: &apos;shared-button&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;template: `
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  &amp;lt;button class=&quot;button&quot; [ngClass]=&quot;&apos;btn-&apos; + buttonTheme&quot; [attr.type]=&quot;buttonType&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &amp;lt;span class=&quot;button-text&quot;&gt;{{ buttonText }}&amp;lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;		 &amp;lt;mat-icon [svgIcon]=&quot;buttonIcon&quot;&gt;&amp;lt;/mat-icon&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  &amp;lt;/button&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;`,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;changeDetection: ChangeDetectionStrategy.OnPush,
&lt;/span&gt;})
export class ButtonComponent {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; @Input() buttonText!: string;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; @Input() buttonIcon!: string;
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However with this implementation, there are always certain constraints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We always use &lt;code class=&quot;language-text&quot;&gt;mat-icon&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The icon always displays after the text&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that, several questions arise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What if I don’t want to use &lt;code class=&quot;language-text&quot;&gt;mat-icon&lt;/code&gt; but used just a font icon with a simple &lt;code class=&quot;language-text&quot;&gt;&amp;lt;i&gt;&lt;/code&gt; tag? Or sometimes, an image with &lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;What if I want to place the icon, then the text?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To support the above requirements, it is so much easier if we can provide both text and icons together as a “&lt;em&gt;template”&lt;/em&gt; to the button component. So that the component’s user can decide how he wants to arrange the content, or which icon library he wants to use.&lt;/p&gt;
&lt;p&gt;We introduce a new &lt;code class=&quot;language-text&quot;&gt;@Input() buttonContent&lt;/code&gt; that accepts a &lt;code class=&quot;language-text&quot;&gt;TemplateRef&lt;/code&gt; instead of just a simple string. We can use something like &lt;code class=&quot;language-text&quot;&gt;content projection&lt;/code&gt; that does the same thing, however, let’s stick with &lt;code class=&quot;language-text&quot;&gt;TemplateRef&lt;/code&gt; for now.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@Component({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;selector: &apos;shared-button&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;template: `
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  &amp;lt;button class=&quot;button&quot; [ngClass]=&quot;&apos;btn-&apos; + buttonTheme&quot; [attr.type]=&quot;buttonType&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &amp;lt;span class=&quot;button-text&quot;&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;      &amp;lt;ng-container&gt; {{ buttonText }} &amp;lt;/ng-container&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;      &amp;lt;ng-container *ngTemplateOutlet=&quot;buttonContent&quot;&gt; &amp;lt;/ng-container&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &amp;lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  &amp;lt;/button&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;`,
&lt;/span&gt;})
export class ButtonComponent {
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; @Input() buttonContent!: TemplateRef&amp;lt;any&gt;;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; @Input() buttonText!: string;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; @Input() buttonTheme: ButtonTheme = &apos;secondary&apos;;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; @Input() buttonType: ButtonType = &apos;button&apos;;
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So with the new implementation, it is very flexible since we can essentially decide what template we want to render inside the button: maybe it is just a single icon, maybe the icon appears in front of the text, and in this example, we use a dedicated component &lt;code class=&quot;language-text&quot;&gt;twitter-icon&lt;/code&gt;. You know what I am talking about.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;shared-button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[buttonContent]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitterBtnTmpl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;#twitterBtnTmpl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Twitter &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;twitter-icon&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn-icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;twitter-icon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;shared-button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Below is how it gets rendered into the DOM. As we can see there is a &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt; that wraps the real native &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; as the previous #1 example.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6635c2c05f65fe1119c83855b98b880f/aa991/02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHKVxYhh//EABUQAQEAAAAAAAAAAAAAAAAAABBB/9oACAEBAAEFAmH/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAbEAACAQUAAAAAAAAAAAAAAAAAARARITFx8P/aAAgBAQABPyGtkPaOwOX/2gAMAwEAAgADAAAAEMQf/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EJWv/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8QiP/EABsQAAEFAQEAAAAAAAAAAAAAAAEAESFRsRBx/9oACAEBAAE/EBKJY6ierxRYTPv/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular augmenting native elements&quot;
        title=&quot;Angular augmenting native elements&quot;
        src=&quot;/static/6635c2c05f65fe1119c83855b98b880f/6a068/02.jpg&quot;
        srcset=&quot;/static/6635c2c05f65fe1119c83855b98b880f/09b79/02.jpg 240w,
/static/6635c2c05f65fe1119c83855b98b880f/7cc5e/02.jpg 480w,
/static/6635c2c05f65fe1119c83855b98b880f/6a068/02.jpg 960w,
/static/6635c2c05f65fe1119c83855b98b880f/644c5/02.jpg 1440w,
/static/6635c2c05f65fe1119c83855b98b880f/aa991/02.jpg 1850w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;custom-button-3&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-button-3&quot; aria-label=&quot;custom button 3 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom button #3&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c5473d61b4159bbea751fb5a9c7fa4d2/12609/Button_3.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe9NCoP/xAAYEAACAwAAAAAAAAAAAAAAAAAAAQIRIP/aAAgBAQABBQIi7z//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAEAAwAAAAAAAAAAAAAAAAABACAx/9oACAEBAAY/ApiV/8QAGRAAAgMBAAAAAAAAAAAAAAAAAAEgMXGB/9oACAEBAAE/IX0ZbZBH/9oADAMBAAIAAwAAABCwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAgMAAAAAAAAAAAAAAAEAESAhQWGR/9oACAEBAAE/EDQpbmhgrqeu/HBLP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Button #3.jpg&quot;
        title=&quot;Button #3.jpg&quot;
        src=&quot;/static/c5473d61b4159bbea751fb5a9c7fa4d2/6a068/Button_3.jpg&quot;
        srcset=&quot;/static/c5473d61b4159bbea751fb5a9c7fa4d2/09b79/Button_3.jpg 240w,
/static/c5473d61b4159bbea751fb5a9c7fa4d2/7cc5e/Button_3.jpg 480w,
/static/c5473d61b4159bbea751fb5a9c7fa4d2/6a068/Button_3.jpg 960w,
/static/c5473d61b4159bbea751fb5a9c7fa4d2/644c5/Button_3.jpg 1440w,
/static/c5473d61b4159bbea751fb5a9c7fa4d2/0f98f/Button_3.jpg 1920w,
/static/c5473d61b4159bbea751fb5a9c7fa4d2/12609/Button_3.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the 3rd use case, things get a little more interesting. We want to have a link, that looks like a button. A link means when click, it opens an URL.&lt;/p&gt;
&lt;p&gt;Looks simple enough, however, the real implementation was a bit more complicated than that.&lt;/p&gt;
&lt;p&gt;A link could be either opened using &lt;code class=&quot;language-text&quot;&gt;routerLink&lt;/code&gt; for an internal Angular route or &lt;code class=&quot;language-text&quot;&gt;href&lt;/code&gt; for an external URL&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We need to check whether the URL includes &lt;code class=&quot;language-text&quot;&gt;http&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;https&lt;/code&gt; to differentiate between internal and external URL&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;routerLink&lt;/code&gt; could require some query parameters&lt;/li&gt;
&lt;li&gt;If the link is external and open with &lt;code class=&quot;language-text&quot;&gt;href&lt;/code&gt;, do we want to open it in a new tab using &lt;code class=&quot;language-text&quot;&gt;target=&quot;_blank&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We also know that if &lt;code class=&quot;language-text&quot;&gt;target=&quot;_blank&quot;&lt;/code&gt; appears, we should set &lt;code class=&quot;language-text&quot;&gt;rel=&quot;noopener noreferrer&quot;&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; tag for &lt;a href=&quot;https://stackoverflow.com/a/50709760/3375906&quot;&gt;safety reasons&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;details&gt;
  &lt;summary&gt;ℹ️ View the full shared-button implementation that supports link&lt;/summary&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonMode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;internalURL&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;externalURL&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;reset&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;submit&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonTheme&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;primary&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;secondary&apos;&lt;/span&gt;

@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;shared-button&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
  &amp;lt;ng-container [ngSwitch]=&quot;buttonMode()&quot;&gt;
    &amp;lt;button
      *ngSwitchCase=&quot;&apos;button&apos;&quot;
      class=&quot;button&quot;
      [ngClass]=&quot;&apos;btn-&apos; + buttonTheme&quot;
      [attr.aria-label]=&quot;ariaLabel&quot;
      [attr.disabled]=&quot;isDisabled ? true : null&quot;
      [attr.type]=&quot;buttonType&quot;
    &gt;
      &amp;lt;img *ngIf=&quot;iconPath&quot; alt=&quot;&quot; class=&quot;btn-icon&quot; [src]=&quot;iconPath&quot; /&gt;
      &amp;lt;span class=&quot;button-text&quot;&gt;
        &amp;lt;ng-container *ngIf=&quot;!!buttonText&quot;&gt; {{ buttonText }} &amp;lt;/ng-container&gt;
        &amp;lt;ng-container *ngTemplateOutlet=&quot;buttonContent&quot;&gt; &amp;lt;/ng-container&gt;
      &amp;lt;/span&gt;
    &amp;lt;/button&gt;

    &amp;lt;a
      *ngSwitchCase=&quot;&apos;internalURL&apos;&quot;
      class=&quot;button&quot;
      [ngClass]=&quot;&apos;btn-&apos; + buttonTheme&quot;
      [routerLink]=&quot;redirectURL&quot;
      [queryParams]=&quot;queryParams&quot;
      [attr.aria-label]=&quot;ariaLabel&quot;
      [attr.role]=&quot;roleType&quot;
    &gt;
      &amp;lt;img *ngIf=&quot;iconPath&quot; class=&quot;btn-icon&quot; [src]=&quot;iconPath&quot; /&gt;
      &amp;lt;span class=&quot;button-text&quot;&gt;
        &amp;lt;ng-container *ngIf=&quot;!!buttonText&quot;&gt; {{ buttonText }} &amp;lt;/ng-container&gt;
        &amp;lt;ng-container *ngTemplateOutlet=&quot;buttonContent&quot;&gt; &amp;lt;/ng-container&gt;
      &amp;lt;/span&gt;
    &amp;lt;/a&gt;

    &amp;lt;a
      *ngSwitchCase=&quot;&apos;externalURL&apos;&quot;
      class=&quot;button&quot;
      [ngClass]=&quot;&apos;btn-&apos; + buttonTheme&quot;
      [href]=&quot;redirectURL&quot;
      [attr.target]=&quot;isTargetBlank ? &apos;_blank&apos; : null&quot;
      [attr.rel]=&quot;isTargetBlank ? &apos;noopener noreferrer&apos; : null&quot;
      [attr.aria-label]=&quot;ariaLabel&quot;
      [attr.role]=&quot;roleType&quot;
    &gt;
      &amp;lt;img *ngIf=&quot;iconPath&quot; class=&quot;btn-icon&quot; [src]=&quot;iconPath&quot; /&gt;
      &amp;lt;span class=&quot;button-text&quot;&gt;
        &amp;lt;ng-container *ngIf=&quot;!!buttonText&quot;&gt; {{ buttonText }} &amp;lt;/ng-container&gt;
        &amp;lt;ng-container *ngTemplateOutlet=&quot;buttonContent&quot;&gt; &amp;lt;/ng-container&gt;
      &amp;lt;/span&gt;
    &amp;lt;/a&gt;
  &amp;lt;/ng-container&gt;
&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;HostBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;class&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; fullButtonClass&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;HostBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;class.shared-button&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; componentClass &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;HostBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;class.is-disabled&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isButtonDisabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDisabled
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonClass&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonContent&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TemplateRef&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonText&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonTheme&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;secondary&apos;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buttonType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; roleType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; redirectURL&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; queryParams&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; isDisabled&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; isTargetBlank&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; iconPath&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaLabel&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; classNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buttonClass&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buttonTheme&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    className &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; className
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullButtonClass &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; classNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;buttonMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonMode &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirectURLExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInternalURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;internalURL&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;externalURL&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redirectURLExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redirectURL
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isInternalURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redirectURL&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redirectURL&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;The following code could render our desired button&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7271b51846f75a368357dc0dba0ed02b/12609/Button_3_-_attr.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGsu8oxof/EABsQAAICAwEAAAAAAAAAAAAAAAABAgMEERQh/9oACAEBAAEFArMqcbO2wXqElo//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAADAAMAAAAAAAAAAAAAAAAAAQIgITH/2gAIAQEABj8CqUlpnJFh/8QAGhABAQEAAwEAAAAAAAAAAAAAAQARIUFR4f/aAAgBAQABPyHNBwsvvPV6SD1G4EX/2gAMAwEAAgADAAAAENwP/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qp//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAECAQE/EIf/xAAdEAACAgEFAAAAAAAAAAAAAAABEQAhYUFRgbHR/9oACAEBAAE/ED+BQNtDmEbax6hyqYCVkQcWF4iFQ9b7hVP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Button #3 - attr.jpg&quot;
        title=&quot;Button #3 - attr.jpg&quot;
        src=&quot;/static/7271b51846f75a368357dc0dba0ed02b/6a068/Button_3_-_attr.jpg&quot;
        srcset=&quot;/static/7271b51846f75a368357dc0dba0ed02b/09b79/Button_3_-_attr.jpg 240w,
/static/7271b51846f75a368357dc0dba0ed02b/7cc5e/Button_3_-_attr.jpg 480w,
/static/7271b51846f75a368357dc0dba0ed02b/6a068/Button_3_-_attr.jpg 960w,
/static/7271b51846f75a368357dc0dba0ed02b/644c5/Button_3_-_attr.jpg 1440w,
/static/7271b51846f75a368357dc0dba0ed02b/0f98f/Button_3_-_attr.jpg 1920w,
/static/7271b51846f75a368357dc0dba0ed02b/12609/Button_3_-_attr.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, the button implementation might have &lt;code class=&quot;language-text&quot;&gt;@Input&lt;/code&gt; with different attributes that you used to work with e.g. &lt;code class=&quot;language-text&quot;&gt;isTargetBlank&lt;/code&gt; instead of just &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;redirectURL&lt;/code&gt; instead normal &lt;code class=&quot;language-text&quot;&gt;href&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Below is how it gets rendered into DOM, we have our &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt; as usual and inside it renders the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;a&gt;&lt;/code&gt; as we are expecting.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8228ebce6ba230073543bc10e7bcc674/af3b8/03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABy5EAf//EABYQAAMAAAAAAAAAAAAAAAAAAAAQEf/aAAgBAQABBQJU/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAGBAAAwEBAAAAAAAAAAAAAAAAAAEhEXH/2gAIAQEAAT8hTlRqOD//2gAMAwEAAgADAAAAEHQv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhAAAwADAQAAAAAAAAAAAAAAAAExEWGBof/aAAgBAQABPxBCXJKi3lGm3Wf/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular augmenting native elements.jpg&quot;
        title=&quot;Angular augmenting native elements.jpg&quot;
        src=&quot;/static/8228ebce6ba230073543bc10e7bcc674/6a068/03.jpg&quot;
        srcset=&quot;/static/8228ebce6ba230073543bc10e7bcc674/09b79/03.jpg 240w,
/static/8228ebce6ba230073543bc10e7bcc674/7cc5e/03.jpg 480w,
/static/8228ebce6ba230073543bc10e7bcc674/6a068/03.jpg 960w,
/static/8228ebce6ba230073543bc10e7bcc674/644c5/03.jpg 1440w,
/static/8228ebce6ba230073543bc10e7bcc674/af3b8/03.jpg 1890w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Also, if you don’t know about the implementation detail at all, it could easily end up in a situation where you render the whole button inside &lt;code class=&quot;language-text&quot;&gt;&amp;lt;a&gt;&lt;/code&gt; tag to have the same anchor behaviour.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://trungvose.com/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;_blank&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
   &amp;lt;shared-button
     [buttonContent]=&quot;readmoreTmpl&quot;
     [buttonTheme]=&quot;&apos;primary&apos;&quot;
   &gt;
     &amp;lt;ng-template #readmoreTmpl&gt;
       Readmore ↗
     &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;shared-button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this case, the DOM structure will be &lt;code class=&quot;language-text&quot;&gt;a &gt; shared-button &gt; button&lt;/code&gt; and because both &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; are focusable, pressing Tab will focus into each one of them.&lt;/p&gt;
&lt;p&gt;In the screenshot, we press Tab to have a default blue outline because we focus on &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt;, press one more time you’ll see our custom outline because now it gets focused on &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/8b40322721415fab7ac07eff403adfab/04.gif&quot; alt=&quot;Angular augmenting native elements&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This approach won’t scale very well&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In all three examples, our implementation is increasingly its complexity when we need to introduce new functionality. However, what we want at the end is truly simple: &lt;strong&gt;apply certain classes to &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; so that it looks the way we want.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For the rest of the code, we are trying to duplicate the functionality of the &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; because they are hidden away within our &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt; component and we couldn’t access it otherwise.&lt;/p&gt;
&lt;h2 id=&quot;global-attributes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#global-attributes&quot; aria-label=&quot;global attributes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Global attributes&lt;/h2&gt;
&lt;p&gt;Because we hide our button element from &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt;, if we want to support new button attributes, we will need to introduce one new &lt;code class=&quot;language-text&quot;&gt;@Input&lt;/code&gt; on our &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/944503415e41c6b404c115c123f12cce/bcec6/06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABbUlEQVQoz62S3W6jMBSEef/Xq7Zq0iwpBgzExwYSSDB/38qOetFqe7VraWQf2R6dmTnJ4XAgTdOI+/3OT2v/Uuw/vksC4el04ng80vc967rivWeaFx7zxn3e8H6GsWVfZ/Z9j+Rx/4ZIeL1eCei6DhHBiCDmQiVXzuZBKhONcVC9st+EfVtjg38jDEicc+zbxv9ayad/WmvKsiTPc4qioNQarSvKUsfaGIOzQu0GajdiRbDWcrlcIsI5dlg3DVmWUVVV/Hg+f0QSpVSsw11ACCx4+/BLhPfT0+tpYvKeeX76mwTvxmFgXZd/lhu8TYIslec0TY10A0pG2v4WA6rrBhEb5UoMy9A6G0OzYmJtXE9mJ+b1mUPyqixvSjgUlrei5SVz/FKWU2HQZclBNZxURVnkvKcZv5UmVSXv54JhHFmWNY7X9jk2xgRjTeyk7Xpc22LEItZFo61zcZS6tqWyN3Ln0Z3nQx5MfvkmeecPSJMAteAL/NkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular augmenting native elements&quot;
        title=&quot;Angular augmenting native elements&quot;
        src=&quot;/static/944503415e41c6b404c115c123f12cce/d9199/06.png&quot;
        srcset=&quot;/static/944503415e41c6b404c115c123f12cce/8ff5a/06.png 240w,
/static/944503415e41c6b404c115c123f12cce/e85cb/06.png 480w,
/static/944503415e41c6b404c115c123f12cce/d9199/06.png 960w,
/static/944503415e41c6b404c115c123f12cce/07a9c/06.png 1440w,
/static/944503415e41c6b404c115c123f12cce/bcec6/06.png 1834w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, each HTML element like &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; will also come with a list of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes&quot;&gt;global attributes ↗&lt;/a&gt; it needs to support.&lt;/p&gt;
&lt;p&gt;It included all ARIA attributes for making our web more accessible, which includes about 50+ more attributes.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f8c30c42b7fee4049986a82cd6533ccf/bcec6/07.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.91666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAABGklEQVQ4y63Sa2uDMBiGYf//b/Orbh7QzjYarTaJMSZ4ekYck8oYs+0CChG5uN+o47ou4jhGGIbwPA9RFCHLMvi+D8YYvteyLDiynCRJQAhBmqa7KwgC1HUNpRT6vt/Qv2BHaw2tDVTfQ0oJbcwK2Od2byu7rjsOci6QpglKWqAqKYqcgFL648XDI0/jiOomcKIMJ8qRlRzGmK3mvupQob01bY+4aJGWEnqYH67ag8uCK+8Q5QJR0WKcph32KOoAC0r2Bb5dBC6NehrbwCtXCHOBdyJghunwef0K1kKthR+VxDw/X7cDbaEd977u6Y9SsQ4BESDr+eH1woor+GeOpjUvYRto/8Mw5+DdP4HFTcI7M4SEQ+rhpZE/AeK6mRcSkSMUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular augmenting native elements&quot;
        title=&quot;Angular augmenting native elements&quot;
        src=&quot;/static/f8c30c42b7fee4049986a82cd6533ccf/d9199/07.png&quot;
        srcset=&quot;/static/f8c30c42b7fee4049986a82cd6533ccf/8ff5a/07.png 240w,
/static/f8c30c42b7fee4049986a82cd6533ccf/e85cb/07.png 480w,
/static/f8c30c42b7fee4049986a82cd6533ccf/d9199/07.png 960w,
/static/f8c30c42b7fee4049986a82cd6533ccf/07a9c/07.png 1440w,
/static/f8c30c42b7fee4049986a82cd6533ccf/bcec6/07.png 1834w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So if our shared-button component suddenly needs to support more attributes, that’s how it might look.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaHidden&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaPlaceholder&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaPressed&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaReadonly&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaRequired&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaSelected&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaValueText&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaControls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaDescribedBy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaDescription&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaFlowTo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
@&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ariaLabelledBy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// and another 100 more Inputs 😏&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;augmenting-native-elements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#augmenting-native-elements&quot; aria-label=&quot;augmenting native elements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Augmenting native elements&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9a9c797c0b3a4841589e953d4aa44933/12609/28.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe7qUA//xAAVEAEBAAAAAAAAAAAAAAAAAAAgQf/aAAgBAQABBQKn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRABAQADAQAAAAAAAAAAAAAAAQAQESFB/9oACAEBAAE/IVePLeGIv//aAAwDAQACAAMAAAAQow//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEBAAEFAAAAAAAAAAAAAAABABEhMWFxkf/aAAgBAQABPxAZActc3R8jaSMlzmSl/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;28.jpg&quot;
        title=&quot;28.jpg&quot;
        src=&quot;/static/9a9c797c0b3a4841589e953d4aa44933/6a068/28.jpg&quot;
        srcset=&quot;/static/9a9c797c0b3a4841589e953d4aa44933/09b79/28.jpg 240w,
/static/9a9c797c0b3a4841589e953d4aa44933/7cc5e/28.jpg 480w,
/static/9a9c797c0b3a4841589e953d4aa44933/6a068/28.jpg 960w,
/static/9a9c797c0b3a4841589e953d4aa44933/644c5/28.jpg 1440w,
/static/9a9c797c0b3a4841589e953d4aa44933/0f98f/28.jpg 1920w,
/static/9a9c797c0b3a4841589e953d4aa44933/12609/28.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;http://angular.io&quot;&gt;angular.io&lt;/a&gt; accessibility guide, there is one small section that mentions &lt;code class=&quot;language-text&quot;&gt;Augmenting native elements&lt;/code&gt; approach.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Native HTML elements capture several standard interaction patterns that are important to accessibility. When authoring Angular components, you should re-use these native elements directly when possible, rather than re-implementing well-supported behaviors.&lt;/p&gt;
&lt;p&gt;For example, instead of creating a custom element for a new variety of button, create a component that uses an attribute selector with a native &lt;code class=&quot;language-text&quot;&gt;&amp;lt;button&gt;&lt;/code&gt; element. This most commonly applies to &lt;code class=&quot;language-text&quot;&gt;&amp;lt;button&gt;&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;&amp;lt;a&gt;&lt;/code&gt;, but can be used with many other types of element.&lt;/p&gt;
&lt;p&gt;From &lt;a href=&quot;https://angular.io/guide/accessibility#augmenting-native-elements&quot;&gt;https://angular.io/guide/accessibility#augmenting-native-elements&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In Angular, &lt;code class=&quot;language-text&quot;&gt;augmenting native elements&lt;/code&gt; is creating a component that uses an &lt;code class=&quot;language-text&quot;&gt;attribute selector&lt;/code&gt; to extend the native &lt;code class=&quot;language-text&quot;&gt;&amp;lt;button&gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;That’s how the code with &lt;code class=&quot;language-text&quot;&gt;augmenting native elements&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Selector: we are not using a custom component tag, instead, we combine &lt;code class=&quot;language-text&quot;&gt;button[shared-button]&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;a[shared-button]&lt;/code&gt; because we don’t want to apply this component to a &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; for example.&lt;/li&gt;
&lt;li&gt;Template: using purely content projection means we want anything which is between a &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Then we can apply directly the class we want to the native element using &lt;code class=&quot;language-text&quot;&gt;HostBinding&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@Component({
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;	 selector: &apos;button[shared-button], a[shared-button]&apos;,
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;	 template: ` &amp;lt;ng-content&gt;&amp;lt;/ng-content&gt; `,
&lt;/span&gt;	 changeDetection: ChangeDetectionStrategy.OnPush,
	 styleUrls: [&apos;./button-v2.component.scss&apos;],
	 encapsulation: ViewEncapsulation.None,
})
export class ButtonV2Component {
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; @HostBinding(&apos;class&apos;) get rdButtonClass(): string {
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  const classes = [&apos;button&apos;, `btn-${this.buttonTheme}`];
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  return classes.filter(Boolean).join(&apos; &apos;);
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;}
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;@Input() buttonTheme: ButtonTheme = &apos;secondary&apos;;
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;re-implement-three-use-cases-using-augmenting-native-elements-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#re-implement-three-use-cases-using-augmenting-native-elements-approach&quot; aria-label=&quot;re implement three use cases using augmenting native elements approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Re-implement three use cases using augmenting native elements approach&lt;/h3&gt;
&lt;p&gt;I show the code side by side so we can have a better comparison. The top is the old approach using a custom &lt;code class=&quot;language-text&quot;&gt;shared-button&lt;/code&gt;, and the bottom uses our new approach of attribute selector.&lt;/p&gt;
&lt;h3 id=&quot;custom-button-1-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-button-1-1&quot; aria-label=&quot;custom button 1 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom button #1&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8fbbd22598c12c611a94dd36ff14fb7e/12609/32.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQCA//EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGnldpagn//xAAaEAACAwEBAAAAAAAAAAAAAAABAwACBBEx/9oACAEBAAEFAmXeLhugweNSvgzp4J//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAcEAACAgIDAAAAAAAAAAAAAAABAgAREBIxM3H/2gAIAQEABj8CaitezlMFtBc6xj//xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhMYH/2gAIAQEAAT8hzy3lQ43o0XN7B1il6xpBwouI/9oADAMBAAIAAwAAABC7z//EABYRAQEBAAAAAAAAAAAAAAAAABEQIf/aAAgBAwEBPxAxn//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/EKf/xAAbEAEBAQACAwAAAAAAAAAAAAABEQAhUTFxsf/aAAgBAQABPxAQMmaZ6t1IuOj9cks0LPFmcBqjyrlJRQvGAITgDo3/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;32.jpg&quot;
        title=&quot;32.jpg&quot;
        src=&quot;/static/8fbbd22598c12c611a94dd36ff14fb7e/6a068/32.jpg&quot;
        srcset=&quot;/static/8fbbd22598c12c611a94dd36ff14fb7e/09b79/32.jpg 240w,
/static/8fbbd22598c12c611a94dd36ff14fb7e/7cc5e/32.jpg 480w,
/static/8fbbd22598c12c611a94dd36ff14fb7e/6a068/32.jpg 960w,
/static/8fbbd22598c12c611a94dd36ff14fb7e/644c5/32.jpg 1440w,
/static/8fbbd22598c12c611a94dd36ff14fb7e/0f98f/32.jpg 1920w,
/static/8fbbd22598c12c611a94dd36ff14fb7e/12609/32.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fa6bdf40f28c6b679f53d79c7c7eebd3/12609/33.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMEBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdk+uJqD/8QAGhABAAEFAAAAAAAAAAAAAAAAAgMAAQQQFP/aAAgBAQABBQJ5EhfVJUauhv8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGxABAAICAwAAAAAAAAAAAAAAAQARAgMgM0H/2gAIAQEABj8CQ0rU6GGSUvnD/8QAGxAAAgIDAQAAAAAAAAAAAAAAAAERIRBR0fD/2gAIAQEAAT8hQcmhO7Pa+EDmS9Mo/9oADAMBAAIAAwAAABBwD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQADAAMBAAAAAAAAAAAAAAEAESExQVGR/9oACAEBAAE/EHkNDkHfEBz6kndAIu15sqVUSk//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;33.jpg&quot;
        title=&quot;33.jpg&quot;
        src=&quot;/static/fa6bdf40f28c6b679f53d79c7c7eebd3/6a068/33.jpg&quot;
        srcset=&quot;/static/fa6bdf40f28c6b679f53d79c7c7eebd3/09b79/33.jpg 240w,
/static/fa6bdf40f28c6b679f53d79c7c7eebd3/7cc5e/33.jpg 480w,
/static/fa6bdf40f28c6b679f53d79c7c7eebd3/6a068/33.jpg 960w,
/static/fa6bdf40f28c6b679f53d79c7c7eebd3/644c5/33.jpg 1440w,
/static/fa6bdf40f28c6b679f53d79c7c7eebd3/0f98f/33.jpg 1920w,
/static/fa6bdf40f28c6b679f53d79c7c7eebd3/12609/33.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;custom-button-2-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-button-2-1&quot; aria-label=&quot;custom button 2 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom button #2&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ff22c56d77bc0583b3fcfd4ed553c88f/12609/34.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAMBBv/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGkdhXSiP/EABwQAAEEAwEAAAAAAAAAAAAAAAMAAQIyBBITI//aAAgBAQABBQIpDsTtkqNTN66so1//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAcEAABAwUAAAAAAAAAAAAAAAAAAQIREEJRcZL/2gAIAQEABj8CcjZicF3Ig7dEP//EABwQAAMBAAIDAAAAAAAAAAAAAAABETEhQXGRof/aAAgBAQABPyFNE+ELjgU6bBL312eT2z5Ef//aAAwDAQACAAMAAAAQxw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAeEAEAAgEEAwAAAAAAAAAAAAABABEhMUFR8GFx0f/aAAgBAQABPxC1RJdSts1PCXe0SGsFvmosuTpP7ED17ZnXcE//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;34.jpg&quot;
        title=&quot;34.jpg&quot;
        src=&quot;/static/ff22c56d77bc0583b3fcfd4ed553c88f/6a068/34.jpg&quot;
        srcset=&quot;/static/ff22c56d77bc0583b3fcfd4ed553c88f/09b79/34.jpg 240w,
/static/ff22c56d77bc0583b3fcfd4ed553c88f/7cc5e/34.jpg 480w,
/static/ff22c56d77bc0583b3fcfd4ed553c88f/6a068/34.jpg 960w,
/static/ff22c56d77bc0583b3fcfd4ed553c88f/644c5/34.jpg 1440w,
/static/ff22c56d77bc0583b3fcfd4ed553c88f/0f98f/34.jpg 1920w,
/static/ff22c56d77bc0583b3fcfd4ed553c88f/12609/34.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;custom-button-3-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-button-3-1&quot; aria-label=&quot;custom button 3 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom button #3&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/63cc35fdac8585952a02e18d3d495941/12609/36.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEEAgb/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABc2p66wRH/8QAGxAAAgIDAQAAAAAAAAAAAAAAAQMAAhEjMTL/2gAIAQEAAQUCc1oaWuMrx43YEr5//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAgIDAAAAAAAAAAAAAAAAAAECERAycf/aAAgBAQAGPwKSUnVmzET7hH//xAAaEAACAwEBAAAAAAAAAAAAAAAAASExoRGR/9oACAEBAAE/IUCiQoXcbvDEKJFRgP/aAAwDAQACAAMAAAAQbA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxAn/8QAHRABAAIBBQEAAAAAAAAAAAAAAQARITFBkaGxwf/aAAgBAQABPxBvIQDQJQUGQWLLOqL4i2TK943PqdR5P//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;36.jpg&quot;
        title=&quot;36.jpg&quot;
        src=&quot;/static/63cc35fdac8585952a02e18d3d495941/6a068/36.jpg&quot;
        srcset=&quot;/static/63cc35fdac8585952a02e18d3d495941/09b79/36.jpg 240w,
/static/63cc35fdac8585952a02e18d3d495941/7cc5e/36.jpg 480w,
/static/63cc35fdac8585952a02e18d3d495941/6a068/36.jpg 960w,
/static/63cc35fdac8585952a02e18d3d495941/644c5/36.jpg 1440w,
/static/63cc35fdac8585952a02e18d3d495941/0f98f/36.jpg 1920w,
/static/63cc35fdac8585952a02e18d3d495941/12609/36.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/12609/37.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECBAX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHdTpyAf//EABkQAAIDAQAAAAAAAAAAAAAAAAEDAhARE//aAAgBAQABBQKbXCfd9Ebf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGxAAAgEFAAAAAAAAAAAAAAAAARAAAxEhM1H/2gAIAQEABj8CIFG47NCy/wD/xAAaEAEBAQADAQAAAAAAAAAAAAABEQAQIVFh/9oACAEBAAE/IZKp69b6+AEFMAECGN//2gAMAwEAAgADAAAAEKMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAQEBAAMAAAAAAAAAAAAAAQARITFRof/aAAgBAQABPxBCeIS8e7IA+sKgpjJSJ5E2NCDgBJS//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;37.jpg&quot;
        title=&quot;37.jpg&quot;
        src=&quot;/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/6a068/37.jpg&quot;
        srcset=&quot;/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/09b79/37.jpg 240w,
/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/7cc5e/37.jpg 480w,
/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/6a068/37.jpg 960w,
/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/644c5/37.jpg 1440w,
/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/0f98f/37.jpg 1920w,
/static/b7057d52deee5fd9fc9a79cb0e8ec0dc/12609/37.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Noticed that in all three examples&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What we render into DOM will be the &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; without a redundant wrapper.&lt;/li&gt;
&lt;li&gt;When we are using the selector, we can access to &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; directly thus it eliminates the need to forward additional attributes. The only &lt;code class=&quot;language-text&quot;&gt;@Input&lt;/code&gt; we accepts so far is &lt;code class=&quot;language-text&quot;&gt;buttonTheme&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-does-it-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-does-it-work&quot; aria-label=&quot;why does it work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why does it work?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://angular.io/api/core/Directive#selector&quot;&gt;Angular directive ↗&lt;/a&gt; selector accepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;element-name&lt;/code&gt;: Select by element name.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;.class&lt;/code&gt;: Select by class name.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[attribute]&lt;/code&gt;: Select by attribute name.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[attribute=value]&lt;/code&gt;: Select by attribute name and value.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;:not(sub_selector)&lt;/code&gt;: Select only if the element does not match the &lt;code class=&quot;language-text&quot;&gt;sub_selector&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;selector1, selector2&lt;/code&gt;: Select if either &lt;code class=&quot;language-text&quot;&gt;selector1&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;selector2&lt;/code&gt; matches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In our code, we use &lt;code class=&quot;language-text&quot;&gt;button[shared-button], a[shared-button]&lt;/code&gt; which is the combination of &lt;code class=&quot;language-text&quot;&gt;element-name&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;[attribute]&lt;/code&gt; selectors.&lt;/p&gt;
&lt;h3 id=&quot;benefits&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#benefits&quot; aria-label=&quot;benefits permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Benefits&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Familiar APIs!&lt;/li&gt;
&lt;li&gt;Accessibility win!&lt;/li&gt;
&lt;li&gt;Simpler implementation!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cdde49614374c1795d670fbc78cce3d7/12609/40.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3psAf//EABYQAAMAAAAAAAAAAAAAAAAAAAARIP/aAAgBAQABBQIU/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAIDH/2gAIAQEABj8CKv8A/8QAGBABAAMBAAAAAAAAAAAAAAAAAQARMSD/2gAIAQEAAT8hRvYO9vBP/9oADAMBAAIAAwAAABBzD//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQACAgMAAAAAAAAAAAAAAAEAERAxQVFh/9oACAEBAAE/EFbA8gEouDqGsVUSz//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;40.jpg&quot;
        title=&quot;40.jpg&quot;
        src=&quot;/static/cdde49614374c1795d670fbc78cce3d7/6a068/40.jpg&quot;
        srcset=&quot;/static/cdde49614374c1795d670fbc78cce3d7/09b79/40.jpg 240w,
/static/cdde49614374c1795d670fbc78cce3d7/7cc5e/40.jpg 480w,
/static/cdde49614374c1795d670fbc78cce3d7/6a068/40.jpg 960w,
/static/cdde49614374c1795d670fbc78cce3d7/644c5/40.jpg 1440w,
/static/cdde49614374c1795d670fbc78cce3d7/0f98f/40.jpg 1920w,
/static/cdde49614374c1795d670fbc78cce3d7/12609/40.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;many-popular-ui-libraries-use-augmenting-native-elements-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#many-popular-ui-libraries-use-augmenting-native-elements-approach&quot; aria-label=&quot;many popular ui libraries use augmenting native elements approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Many popular UI libraries use augmenting native elements approach&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Angular Material&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/angular/components/blob/main/src/material/button/button.ts#L40&quot;&gt;material/button/button.ts#L40&lt;/a&gt; ↗️&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
   button[mat-button], button[mat-raised-button], button[mat-flat-button],
   button[mat-stroked-button]
 &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;button.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 inputs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAT_BUTTON_INPUTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 exportAs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;matButton&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 encapsulation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ViewEncapsulation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;None&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 changeDetection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ChangeDetectionStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OnPush&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatButton&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatButtonBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;NG-ZORRO&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/NG-ZORRO/ng-zorro-antd/blob/d54b3b4cc44b6e9404a2de1e75ece5c3928ec453/components/button/button.component.ts#L40&quot;&gt;components/button/button.component.ts#L40&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Notice here ng-zorro enhance the template to supply a loading icon as well, not just a simple &lt;code class=&quot;language-text&quot;&gt;ng-content&lt;/code&gt; anymore.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@Component({
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;selector: &apos;button[nz-button], a[nz-button]&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;exportAs: &apos;nzButton&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;changeDetection: ChangeDetectionStrategy.OnPush,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;encapsulation: ViewEncapsulation.None,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;template: `
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  &amp;lt;i nz-icon nzType=&quot;loading&quot; *ngIf=&quot;nzLoading&quot;&gt;&amp;lt;/i&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;  &amp;lt;ng-content&gt;&amp;lt;/ng-content&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;`,
&lt;/span&gt;})
export class NzButtonComponent implements OnDestroy, OnChanges,&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;what-else-can-you-do-with-augmenting-native-elements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-else-can-you-do-with-augmenting-native-elements&quot; aria-label=&quot;what else can you do with augmenting native elements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What else can you do with augmenting native elements?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Angular Material&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/angular/components/blob/main/src/material/table/table.ts#L41&quot;&gt;material/table/table.ts#L41&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mat-table, table[mat-table]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 exportAs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;matTable&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CDK_TABLE_TEMPLATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 providers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;provide&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CdkTable&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useExisting&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MatTable&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;provide&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CDK_TABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useExisting&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MatTable&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 changeDetection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ChangeDetectionStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Default&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatTable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CdkTable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/angular/components/blob/main/src/material/tabs/tab-nav-bar/tab-nav-bar.ts#L307&quot;&gt;material/tabs/tab-nav-bar/tab-nav-bar.ts#L307&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[mat-tab-nav-bar]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 exportAs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;matTabNavBar, matTabNav&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tab-nav-bar.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 encapsulation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ViewEncapsulation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;None&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 changeDetection&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ChangeDetectionStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Default&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MatTabNav&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;_MatTabNavBase&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;-should-we-never-replace-native-components-with-custom-components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-should-we-never-replace-native-components-with-custom-components&quot; aria-label=&quot; should we never replace native components with custom components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;❓ Should we never replace native components with custom components?&lt;/h3&gt;
&lt;p&gt;No. We need custom components!&lt;/p&gt;
&lt;p&gt;However, when you’re creating a new component, you should ask yourself&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Can I augment an existing one instead? ✅&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Core Web Vitals]]></title><description><![CDATA[Introduction to Core Web Vitals]]></description><link>https://trungvose.comcore-web-vitals/</link><guid isPermaLink="false">https://trungvose.comcore-web-vitals/</guid><pubDate>Mon, 01 May 2023 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;As I mentioned on the previous two articles, &lt;a href=&quot;/blog/why-web-performance-matters-an-introduction&quot;&gt;Why Web Performance Matters: An Introduction&lt;/a&gt; and &lt;a href=&quot;/blog/perceived-performance&quot;&gt;Perceived Performance&lt;/a&gt;, web performance is crucial for user experience and business success. In this article, we’ll explore how to measure web performance using Core Web Vitals.&lt;/p&gt;
&lt;p&gt;Shout-out to &lt;a href=&quot;https://frontendmasters.com/courses/web-perf&quot;&gt;Web Performance Fundamentals&lt;/a&gt; for providing a comprehensive guide on web performance.&lt;/p&gt;
&lt;h2 id=&quot;page-load-time&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#page-load-time&quot; aria-label=&quot;page load time permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Page Load Time&lt;/h2&gt;
&lt;p&gt;In the beginning, there was PageLoad. Website performance was measured with a single measurement of the time until the PageLoad event is fired.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5057bfeae69a98967430d2dd1d64691d/12609/mearsure-performance-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAey1goH/xAAZEAADAAMAAAAAAAAAAAAAAAAAARAREiH/2gAIAQEAAQUCOCeZqp//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAXEAADAQAAAAAAAAAAAAAAAAAAIEGR/9oACAEBAAY/AqXV/8QAGxAAAwACAwAAAAAAAAAAAAAAAAERITFRgZH/2gAIAQEAAT8hwllL2UnAhvY2OxeCUUP/2gAMAwEAAgADAAAAEJDv/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8Qh//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxA1f//EAB0QAQADAAEFAAAAAAAAAAAAAAEAESExQVFh4fD/2gAIAQEAAT8QyU3n3h0ubfy4IW2q2tjigJ2SXphwUhiAAygqf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Page Load Time&quot;
        title=&quot;Page Load Time&quot;
        src=&quot;/static/5057bfeae69a98967430d2dd1d64691d/6a068/mearsure-performance-01.jpg&quot;
        srcset=&quot;/static/5057bfeae69a98967430d2dd1d64691d/09b79/mearsure-performance-01.jpg 240w,
/static/5057bfeae69a98967430d2dd1d64691d/7cc5e/mearsure-performance-01.jpg 480w,
/static/5057bfeae69a98967430d2dd1d64691d/6a068/mearsure-performance-01.jpg 960w,
/static/5057bfeae69a98967430d2dd1d64691d/644c5/mearsure-performance-01.jpg 1440w,
/static/5057bfeae69a98967430d2dd1d64691d/0f98f/mearsure-performance-01.jpg 1920w,
/static/5057bfeae69a98967430d2dd1d64691d/12609/mearsure-performance-01.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But PageLoad doesn’t fully describe performance. Some sites initially load really fast, but dynamic content needs to load. PageLoad doesn’t fully capture whether a website feels fast.&lt;/p&gt;
&lt;p&gt;Worse, PageLoad was easy to manipulate. Developers could improve their PageLoad time by deferring work with JavaScript. Lazy-loading, async script loaders, client-side rendering, and dynamic content were all patterns that often improved PageLoad time, but created a slower experience from the end user perspective.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a65f5ca27d657f4cea09009bddd93811/12609/mearsure-performance-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAey1goH/xAAYEAADAQEAAAAAAAAAAAAAAAAAARASEf/aAAgBAQABBQI0J9mVP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABcQAAMBAAAAAAAAAAAAAAAAAAAgQUL/2gAIAQEABj8C0Vf/xAAaEAADAQEBAQAAAAAAAAAAAAAAARExQYGR/9oACAEBAAE/IcXAp43qILIbo2OxfBKKH//aAAwDAQACAAMAAAAQUO//xAAVEQEBAAAAAAAAAAAAAAAAAAAQIf/aAAgBAwEBPxCH/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/EDV//8QAHBABAAMAAgMAAAAAAAAAAAAAAQARITFRYcHR/9oACAEBAAE/EAqXo1IAo08v0nOngp6jigJ0kvTDgpDEABlBU//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Page Load Time&quot;
        title=&quot;Page Load Time&quot;
        src=&quot;/static/a65f5ca27d657f4cea09009bddd93811/6a068/mearsure-performance-02.jpg&quot;
        srcset=&quot;/static/a65f5ca27d657f4cea09009bddd93811/09b79/mearsure-performance-02.jpg 240w,
/static/a65f5ca27d657f4cea09009bddd93811/7cc5e/mearsure-performance-02.jpg 480w,
/static/a65f5ca27d657f4cea09009bddd93811/6a068/mearsure-performance-02.jpg 960w,
/static/a65f5ca27d657f4cea09009bddd93811/644c5/mearsure-performance-02.jpg 1440w,
/static/a65f5ca27d657f4cea09009bddd93811/0f98f/mearsure-performance-02.jpg 1920w,
/static/a65f5ca27d657f4cea09009bddd93811/12609/mearsure-performance-02.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;What do we do instead?&lt;/p&gt;
&lt;p&gt;There are lots of ways a website can feel slow: slow to start, slow to finish, jumping around, slow to respond, and more. We can’t use one metric to understand performance anymore.&lt;/p&gt;
&lt;h2 id=&quot;core-web-vitals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#core-web-vitals&quot; aria-label=&quot;core web vitals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Core Web Vitals&lt;/h2&gt;
&lt;p&gt;In 2019, Google introduced a set of metrics intent on measuring the actual performance of a website as the users would see it. These metrics are collectively called the &lt;a href=&quot;https://web.dev/articles/vitals&quot;&gt;Core Web Vitals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a0af6959cff7fb12eb77166fb66dedc/12609/mearsure-performance-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHspeY5of/EABgQAQEBAQEAAAAAAAAAAAAAAAEAERIC/9oACAEBAAEFArQvL0XAoZf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAaEAACAwEBAAAAAAAAAAAAAAAAAREhkRAx/9oACAEBAAY/AvHpLT0nl2Uf/8QAGBABAQEBAQAAAAAAAAAAAAAAAQARMVH/2gAIAQEAAT8hRKpEA91CI5IvHJqgOcX/2gAMAwEAAgADAAAAEHjv/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAQEBAQEBAAAAAAAAAAAAAREAITFRYf/aAAgBAQABPxAEvxYmYxF7WDBaD3cD9VLniTgOk0jQeHzf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Core Web Vitals&quot;
        title=&quot;Core Web Vitals&quot;
        src=&quot;/static/1a0af6959cff7fb12eb77166fb66dedc/6a068/mearsure-performance-03.jpg&quot;
        srcset=&quot;/static/1a0af6959cff7fb12eb77166fb66dedc/09b79/mearsure-performance-03.jpg 240w,
/static/1a0af6959cff7fb12eb77166fb66dedc/7cc5e/mearsure-performance-03.jpg 480w,
/static/1a0af6959cff7fb12eb77166fb66dedc/6a068/mearsure-performance-03.jpg 960w,
/static/1a0af6959cff7fb12eb77166fb66dedc/644c5/mearsure-performance-03.jpg 1440w,
/static/1a0af6959cff7fb12eb77166fb66dedc/0f98f/mearsure-performance-03.jpg 1920w,
/static/1a0af6959cff7fb12eb77166fb66dedc/12609/mearsure-performance-03.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;They are measured in all Chrome-based browsers, including the Googlebot spider, which uses these scores to influence page rank.&lt;/p&gt;
&lt;p&gt;Note that Chrome-based browsers, Firefox, and Safari support these metrics and compatibility is increasing. You can check the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#browser-support&quot;&gt;current compatibility of the Core Web Vitals here&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The web-vitals code has been tested and will run without error in all major browsers as well as Internet Explorer back to version 9. However, some of the APIs required to capture these metrics are currently only available in Chromium-based browsers (e.g. Chrome, Edge, Opera, Samsung Internet).&lt;/p&gt;
&lt;p&gt;Browser support for each function is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;onCLS(): Chromium&lt;/li&gt;
&lt;li&gt;onFCP(): Chromium, Firefox, Safari&lt;/li&gt;
&lt;li&gt;onFID(): Chromium, Firefox (Deprecated)&lt;/li&gt;
&lt;li&gt;onINP(): Chromium&lt;/li&gt;
&lt;li&gt;onLCP(): Chromium, Firefox&lt;/li&gt;
&lt;li&gt;onTTFB(): Chromium, Firefox, Safari&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The metrics that make up Core Web Vitals will &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/README.md&quot;&gt;evolve&lt;/a&gt; over time. The current set focuses on three aspects of the user experience—loading, interactivity, and visual stability—and includes the following metrics (and their respective thresholds):&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e8a24695bac4d79b0c961b1d7edf93ea/9c5f4/mearsure-performance-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 26.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd2AsD//xAAYEAACAwAAAAAAAAAAAAAAAAABESExQf/aAAgBAQABBQLZQr//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAZEAADAAMAAAAAAAAAAAAAAAAAASERQVH/2gAIAQEAAT8hu4lkp0P/2gAMAwEAAgADAAAAEAPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAQADAQEAAAAAAAAAAAAAAREAUdEhMf/aAAgBAQABPxCXZmo9yRn1+x7hQCrvP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Core Web Vitals&quot;
        title=&quot;Core Web Vitals&quot;
        src=&quot;/static/e8a24695bac4d79b0c961b1d7edf93ea/6a068/mearsure-performance-04.jpg&quot;
        srcset=&quot;/static/e8a24695bac4d79b0c961b1d7edf93ea/09b79/mearsure-performance-04.jpg 240w,
/static/e8a24695bac4d79b0c961b1d7edf93ea/7cc5e/mearsure-performance-04.jpg 480w,
/static/e8a24695bac4d79b0c961b1d7edf93ea/6a068/mearsure-performance-04.jpg 960w,
/static/e8a24695bac4d79b0c961b1d7edf93ea/644c5/mearsure-performance-04.jpg 1440w,
/static/e8a24695bac4d79b0c961b1d7edf93ea/9c5f4/mearsure-performance-04.jpg 1806w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/articles/lcp&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;: measures loading performance. To provide a good user experience, LCP should occur within &lt;code class=&quot;language-text&quot;&gt;2.5&lt;/code&gt; seconds of when the page first starts loading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/articles/inp&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;: measures interactivity. To provide a good user experience, pages should have a INP of &lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; milliseconds or less.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/articles/cls&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;: measures visual stability. To provide a good user experience, pages should maintain a CLS of &lt;code class=&quot;language-text&quot;&gt;0.1&lt;/code&gt;. or less.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might have heard of First Input Delay (FID), however it is deprecated September 9 2024 in favor of Interaction to Next Paint (INP).&lt;/p&gt;
&lt;p&gt;While First Contentful Paint (FCP) is not one of the Core Web Vitals that impact Google rankings, it is still one of Google’s broader set of Web Vitals metrics. The Largest Contentful Paint (LCP) is one of the Core Web Vitals metrics, and it can never be lower than the First Contentful Paint (FCP).&lt;/p&gt;
&lt;h3 id=&quot;largest-contentful-paint-lcp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#largest-contentful-paint-lcp&quot; aria-label=&quot;largest contentful paint lcp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Largest Contentful Paint (LCP)&lt;/h3&gt;
&lt;p&gt;“Largest Contentful Paint” measures how long it takes until the browser renders the largest amount of content to the screen. At this point, ideally, the user can see the content they are looking for and believes the page is nearly done.&lt;/p&gt;
&lt;p&gt;Like the First Paint and First Contentful Paint metrics, LCP is a paint timing metric that marks a rendering milestone in the page load process.&lt;/p&gt;
&lt;p&gt;Contentful means that content like text or an image were rendered, rather than just showing empty boxes or background images.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/269b57cbc4562fc1f98c9210fa755933/8c557/lcp-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACj0lEQVQoz02SS09TURRG+ydMlFeMQtrSYkBjok4cO9KJP0Anjh3IhLYY2nKBIqU+kGgkUUBMIOHVlttbagwMDIQIlLYBwyOUiC2EYu+j0CJd5pZoPMmXnX0e39n7nGW44G/i0lQzl8WnlAWaKAvaMEpOLorNVATtlAdtmCRXab5KtFMTbuZKRKBKdGCMOLg24+LmbAfmyFPMkgvDs7UIg9vzDGzP44pP0b4UoDf2mZcr0zQujNC2FOBVLEJXVOTBlyHujQ3zcPoDj2b7qenr49zzt5jf+6ge6MEUcmE4yKv8HZvKPrIiQ74Ix79ZTG+R13Jn+VGeaCqJ8uuYvHKEpsrc7v2KSZjjlneBKz0hjCEnhnVlr2RWLBb5lkmSPNxD0zRkVWFmd5WUnEHVVA6ULPPpDdScwlEux085g5hYZX17l9SezKf4IqZQC4YNZf+svCIsZpLsHO6T0zQUVS0ZprMZNFUjo2SZS22QVZXShT9+HTC3vUVOPqRYKDCxGcckOTGkjuR/LX/PpjlUslA4hfwJC+lNcnrLhVNOcsfE9nfguAD5UxRNRYxtEv2eZj2pML4Wxyy1YGiKTdK5No1nbZony6M4ViZpXRZxRad4vDhMa0zEEw3TlhBpXBqlPRHCk5BwxYPcDw9yZ2yI+oGP3I30U6tXWKmjEbCVVBWyU/XGjdHtpbrTQ8W4g3K/nfMjdip0bAIOykUble+cWDq6sXZ5aej2cbXLh/Gl5+xTLJIbq+RGj5awG0uwlbqAQO2oQIP3Bdc7e7jp7eVG12vqfD5qJRd1UwL1/nasE21YJ9tK0TIpYNINdRh16Q+qc2QOOzHpkpzU+lux+gWsAQGLXyjl+iF93RhuOdv3n3Sw/wCL6lPvwEkoXQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Largest Contentful Paint&quot;
        title=&quot;Largest Contentful Paint&quot;
        src=&quot;/static/269b57cbc4562fc1f98c9210fa755933/8c557/lcp-01.png&quot;
        srcset=&quot;/static/269b57cbc4562fc1f98c9210fa755933/8ff5a/lcp-01.png 240w,
/static/269b57cbc4562fc1f98c9210fa755933/e85cb/lcp-01.png 480w,
/static/269b57cbc4562fc1f98c9210fa755933/8c557/lcp-01.png 700w&quot;
        sizes=&quot;(max-width: 700px) 100vw, 700px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the website filmstrip above the LCP element is a piece of text, but it’s also common for the LCP to be caused by an image like in the example below.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3b2b3868a76aeab1eca9c6d337ae331f/f43e4/lcp-02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACMklEQVQoz5WS32vTUBzF84f46D/jsy8qDKZTBoO56XT6sM7JcO4HglrYD8WJ+uJ/oGxsMFecVfaLVduuWZq2aZs0XbI21DZtk4/cdBUHe/HC5YRzL+d8c86VFEWhXC6j6zonJyds7+wyNPKQsYkn9PYPM3gvRM+Vy/Te6OfR5DQ3B0YYfjDBres9XL3WQ+jxFP2DowzcGWN4cACpVqvx7yoUi4zPPONpeJHp8CJzi8sM3e5jdDzE7MJrZueXePHmPaHQCEP37zIz/4q5hSVeLn9gamYSqVKpBELtdjtAMe1ubI9sMYucOQpw52eMrb0vKAWFuCqj5lWOclnWvq6QUOPE0ykO1UOUrIJUrVYDIc/zArQsC9uyz0xtn8OJ9SuZoFqr0G67+J5H3Tk+X9AwjDNTi3zFDjivwzUaDRKHCaoNl2Ra4XejhedzvmCpVDrDCYOuSZdz3TppNQ+Y/Fi/iKk+D/i/Gf6vYL1eJ5/XUZRdwrOX+PhulFbLR3Ich9ObnbxsG9M0g2/f9wMUBl2T7r2m2yCXP8Yx1om8vcDepz7EiSRabQlH30ekoxsGWqEQHNabzQC1YhFN0zqc79MEHKdKKqXiunnMdJhKOYLrCUFdx97fx9rcxI5GMTUNM5PBjsexUilsWcZMJjFLJex0GmtjA3t7m+NyGaNkYCdkzG8x7M3vWLksEqLJWAwiEYhGRdogipJlUS9kMlAsdn43l4PVVTg4gNMXQC4LK58huiWq5w/mShVmcA2MDgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Largest Contentful Paint&quot;
        title=&quot;Largest Contentful Paint&quot;
        src=&quot;/static/3b2b3868a76aeab1eca9c6d337ae331f/d9199/lcp-02.png&quot;
        srcset=&quot;/static/3b2b3868a76aeab1eca9c6d337ae331f/8ff5a/lcp-02.png 240w,
/static/3b2b3868a76aeab1eca9c6d337ae331f/e85cb/lcp-02.png 480w,
/static/3b2b3868a76aeab1eca9c6d337ae331f/d9199/lcp-02.png 960w,
/static/3b2b3868a76aeab1eca9c6d337ae331f/f43e4/lcp-02.png 1120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Another example, navigating to the homepage of NPR News has a few different renders, but this is the largest one by pixel area:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/342e8b25e2aa7007207c66ce057b4e85/12609/mearsure-performance-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAIBAwQF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB69TIbCQ//8QAGxABAAICAwAAAAAAAAAAAAAAAQACAxIhMTL/2gAIAQEAAQUC5G2RIdX9huz/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAACAwEAAAAAAAAAAAAAAAAAIRARseH/2gAIAQEABj8C6LYplOP/xAAbEAACAwEBAQAAAAAAAAAAAAABEQAhQTFRYf/aAAgBAQABPyFVGvYFaP2HR9haWGGCI9LsuDk//9oADAMBAAIAAwAAABA0L//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAwEBPxARf//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/EKf/xAAdEAACAgIDAQAAAAAAAAAAAAABEQAhQVFhobHh/9oACAEBAAE/ECRAHgAPyA1WI/ERlylsvuW0E2nJ4h2AzWLWoKTU/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Largest Contentful Paint&quot;
        title=&quot;Largest Contentful Paint&quot;
        src=&quot;/static/342e8b25e2aa7007207c66ce057b4e85/6a068/mearsure-performance-05.jpg&quot;
        srcset=&quot;/static/342e8b25e2aa7007207c66ce057b4e85/09b79/mearsure-performance-05.jpg 240w,
/static/342e8b25e2aa7007207c66ce057b4e85/7cc5e/mearsure-performance-05.jpg 480w,
/static/342e8b25e2aa7007207c66ce057b4e85/6a068/mearsure-performance-05.jpg 960w,
/static/342e8b25e2aa7007207c66ce057b4e85/644c5/mearsure-performance-05.jpg 1440w,
/static/342e8b25e2aa7007207c66ce057b4e85/0f98f/mearsure-performance-05.jpg 1920w,
/static/342e8b25e2aa7007207c66ce057b4e85/12609/mearsure-performance-05.jpg 3000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The advertisement is probably not what the user is looking for, but the article images might be.&lt;/p&gt;
&lt;p&gt;LCP encourages websites to &lt;em&gt;finish quickly&lt;/em&gt; by emphasizing their primary content and making sure it loads fast.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For optimal user experience and search engine performance, websites should aim for an LCP of &lt;code class=&quot;language-text&quot;&gt;2.5&lt;/code&gt; seconds or less. Google considers an LCP of &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt; seconds or more to be poor, which can worsen your search rankings and user experience.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;cumulative-layout-shift-cls&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cumulative-layout-shift-cls&quot; aria-label=&quot;cumulative layout shift cls permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cumulative Layout Shift (CLS)&lt;/h3&gt;
&lt;p&gt;“Cumulative Layout Shift” is a little harder to understand because it does not measure time. CLS measures how much the content on a page moves around as other content is loaded and rendered. Like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/b0021bd68f12530d41a295e2f8870a39/cls-ads.gif&quot; alt=&quot;Cumulative Layout Shift&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Layout Shifts measure how late-rendered content affects the user experience of a page. Layout shifts that push important content around are really frustrating to use.&lt;/p&gt;
&lt;p&gt;CLS discourages websites from moving content around once the user sees it and minimizing the amount of late-rendered content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Layout shift score&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have a CLS score of &lt;code class=&quot;language-text&quot;&gt;0.1&lt;/code&gt; or less&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To calculate the layout shift score, the browser looks at the viewport size and the movement of unstable elements in the viewport between two rendered frames. The layout shift score is a product of two measures of that movement: the impact fraction and the distance fraction (both defined below).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;layout shift score = impact fraction * distance fraction&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;impact fraction: the area of the viewport affected by the layout shift (impact size / viewport size)&lt;/li&gt;
&lt;li&gt;distance fraction: the distance the unstable element moved in the viewport (distance / viewport size)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See the example below:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8c0bd0aa5742b3ba4e761d3986ac1504/f149b/cls-combine.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.166666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABgoIsFf/EABcQAQEBAQAAAAAAAAAAAAAAABICEQP/2gAIAQEAAQUCN5T3gx//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAYEAADAQEAAAAAAAAAAAAAAAAAATERof/aAAgBAQAGPwK9Koh61T//xAAaEAACAgMAAAAAAAAAAAAAAAAAAREhMbHB/9oACAEBAAE/IcDYQxo4IcAf/9oADAMBAAIAAwAAABB4D//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/EKf/xAAVEQEBAAAAAAAAAAAAAAAAAAARAP/aAAgBAgEBPxAYv//EABsQAQEAAwADAAAAAAAAAAAAAAERACExUWGR/9oACAEBAAE/EFbFQ7LGUxSXb8vObpG5A5D1n//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cumulative Layout Shift&quot;
        title=&quot;Cumulative Layout Shift&quot;
        src=&quot;/static/8c0bd0aa5742b3ba4e761d3986ac1504/6a068/cls-combine.jpg&quot;
        srcset=&quot;/static/8c0bd0aa5742b3ba4e761d3986ac1504/09b79/cls-combine.jpg 240w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/7cc5e/cls-combine.jpg 480w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/6a068/cls-combine.jpg 960w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/644c5/cls-combine.jpg 1440w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/0f98f/cls-combine.jpg 1920w,
/static/8c0bd0aa5742b3ba4e761d3986ac1504/f149b/cls-combine.jpg 3618w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/497bbf179e5c118c9a89c31337a29def/d6f75/cls-layout-shift-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3ViVD//EABYQAAMAAAAAAAAAAAAAAAAAAAAgIf/aAAgBAQABBQIq/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAbEAABBAMAAAAAAAAAAAAAAAAAAREgIVFhgf/aAAgBAQABPyG9nQj5h//aAAwDAQACAAMAAAAQgw//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAWEQADAAAAAAAAAAAAAAAAAAABECH/2gAIAQIBAT8QEX//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEhkSBBcf/aAAgBAQABPxBzTaiWnqeEFMrRw//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cumulative Layout Shift&quot;
        title=&quot;Cumulative Layout Shift&quot;
        src=&quot;/static/497bbf179e5c118c9a89c31337a29def/6a068/cls-layout-shift-score.jpg&quot;
        srcset=&quot;/static/497bbf179e5c118c9a89c31337a29def/09b79/cls-layout-shift-score.jpg 240w,
/static/497bbf179e5c118c9a89c31337a29def/7cc5e/cls-layout-shift-score.jpg 480w,
/static/497bbf179e5c118c9a89c31337a29def/6a068/cls-layout-shift-score.jpg 960w,
/static/497bbf179e5c118c9a89c31337a29def/644c5/cls-layout-shift-score.jpg 1440w,
/static/497bbf179e5c118c9a89c31337a29def/0f98f/cls-layout-shift-score.jpg 1920w,
/static/497bbf179e5c118c9a89c31337a29def/d6f75/cls-layout-shift-score.jpg 8000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Another example is the layout shift score for a mobile view:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2fe3c0361a755bad936119b2641866ae/2fab2/cls-mobile-score.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBBAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAdRRMrhBX//EABoQAAIDAQEAAAAAAAAAAAAAAAERAgMSMjP/2gAIAQEAAQUCtepsiHNz2WI1ef8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAgEFAQAAAAAAAAAAAAAAAAECESEiMUFx/9oACAEBAAY/ArSa8I5M3UtGTtwji9cFVH//xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhUTH/2gAIAQEAAT8hqqTwYpea1NLms3WRpJwFkvfqPCtBPjP/2gAMAwEAAgADAAAAEEsv/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qp//EABYRAQEBAAAAAAAAAAAAAAAAAAEAUf/aAAgBAgEBPxBXIv/EABoQAQADAQEBAAAAAAAAAAAAAAEAESExYUH/2gAIAQEAAT8QIwAym2+jEWqPpL3JlkLGi3ftQBQhtwNvdmzuUFVndgNp7UaTXs//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cumulative Layout Shift&quot;
        title=&quot;Cumulative Layout Shift&quot;
        src=&quot;/static/2fe3c0361a755bad936119b2641866ae/6a068/cls-mobile-score.jpg&quot;
        srcset=&quot;/static/2fe3c0361a755bad936119b2641866ae/09b79/cls-mobile-score.jpg 240w,
/static/2fe3c0361a755bad936119b2641866ae/7cc5e/cls-mobile-score.jpg 480w,
/static/2fe3c0361a755bad936119b2641866ae/6a068/cls-mobile-score.jpg 960w,
/static/2fe3c0361a755bad936119b2641866ae/644c5/cls-mobile-score.jpg 1440w,
/static/2fe3c0361a755bad936119b2641866ae/0f98f/cls-mobile-score.jpg 1920w,
/static/2fe3c0361a755bad936119b2641866ae/2fab2/cls-mobile-score.jpg 2644w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;interaction-to-next-paint-inp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#interaction-to-next-paint-inp&quot; aria-label=&quot;interaction to next paint inp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Interaction to Next Paint (INP)&lt;/h3&gt;
&lt;p&gt;Interaction to Next Paint (INP) is a web performance metric that measures user interface responsiveness – how quickly a website responds to user interactions like clicks or key presses.&lt;/p&gt;
&lt;p&gt;Specifically, it measures how much time elapses between a user interaction like a click or key press and the next time the user sees a visual update on the page.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 664px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0760378ab5b83f1b2b46114f33190296/31493/inp-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABs0lEQVQoz11Sy47TQBD0X3PYAzcufAOHXOCAQIs4oGglRBAKhyUSl0jxOl5t7Jn4kfErnhk73owLdZsNK1ryq6ampqvLnrU93Ag4ugGw1sLYjt8nBDDGQpuOeeM4oVprnE6niTc6jM7xmldmMVolkcgYh8MB1mgUyQ5HtYcUMcqyhNFHFMkDYyKO0NQNqqrCXkrkeY40V9wECXsflhJvbiJsHnLopoSqDd7/iPH2W4T7OINuKkilMfsa4913AZnkaI8NmqZBcHeHoiiQh79RZTE78l7PW7z42CI4OG5fFD1efm5x9UkjPU6W/b3F1XWLV18M9OQSShW4D0PYrof4eY3EXzHuAZMQxmkGw0A7HE+QLLhxxCNj44T/nWFd15BSIkkSFiWecw7eMDziqSbB4TJ4KiL9j1GR5TiK+EnhEO98PlOHQJqm8H3/X8rGYLPZIAgCxrqu4+v29hcHRwcQj8Tm8zkOeX453Ov7HmEYYrlcQinFqRJxvV5jtVrxtxAC0W6HxWLBqZIYpUxNzGYzbLfbi0OP5kBClJYU4vJvPc2Jus+yjNeoy+dF4m3b8l6yS6H8AZI+qK9noaf7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Interaction to Next Paint&quot;
        title=&quot;Interaction to Next Paint&quot;
        src=&quot;/static/0760378ab5b83f1b2b46114f33190296/31493/inp-01.png&quot;
        srcset=&quot;/static/0760378ab5b83f1b2b46114f33190296/8ff5a/inp-01.png 240w,
/static/0760378ab5b83f1b2b46114f33190296/e85cb/inp-01.png 480w,
/static/0760378ab5b83f1b2b46114f33190296/31493/inp-01.png 664w&quot;
        sizes=&quot;(max-width: 664px) 100vw, 664px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How is Interaction to Next Paint measured?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;INP measures the responsiveness of your website by tracking the time between user input and UI updates. This metric consists of three key components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input Delay: waiting for background tasks on the page that prevent the event handler from running&lt;/li&gt;
&lt;li&gt;Processing Time: running event handlers in JavaScript&lt;/li&gt;
&lt;li&gt;Presentation Delay: handling other queued up interactions, recalculating the page layout, and painting page content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This diagram shows an example timeline of different CPU tasks, and how they add up to INP activity. Interaction to Next Paint spans the entire time frame from the mouse, touch, or keyboard input up to the point when the next frame is rendered by the browser.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 634px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1c324fae1c172e57d7bfdc6dd2614df2/374ac/inp-02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABiElEQVQoz52RPYsTURSG59e5Frb2CioIW/jxA8SvwsZCC/sUYqPVKsaxMcHG1cVNBANZzIiZJDtfdz4yuTNz595HZlazYNRFX3jgFC/vOS/H0sZQ6SOaWbVAoWr8KCLwA4pcQm0wSm9Qa02UxkwXc5IsxeKP0qAKNCWKE1QBed1cgdX1FecHsuVdXPPQKTk3LNn+EHKhP2Kn85HVzT7e3T7B7WP8Oz3SG30e9V5zKbC56L6kEw2xni4Up3dXLW9Cxa1xwdb7grNvPbae79G53yO5/ILZlS7+NRv/asMrvOs26bbNPXuHM/NnnBo/5oG/+7fKxzJtK0NUZcRqSVLniCpDqCUyzWCl1kbLLyGUirIsqZRiJSVFpdCm+YNBN4OBqqwQYUQcCZI4Wc9BEJDly3Zp81TryWFB1zlk9uWAb66L53kIIY4WGrPmxBY/fNZgf8Gn4Vc8b47rzpBSbph+Df8dPz3WeHDA5/0RU3eK4zhMJpONC/9F1p72GeUeWSiIhGjD4jj+78DvPInwEvSy668AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Interaction to Next Paint&quot;
        title=&quot;Interaction to Next Paint&quot;
        src=&quot;/static/1c324fae1c172e57d7bfdc6dd2614df2/374ac/inp-02.png&quot;
        srcset=&quot;/static/1c324fae1c172e57d7bfdc6dd2614df2/8ff5a/inp-02.png 240w,
/static/1c324fae1c172e57d7bfdc6dd2614df2/e85cb/inp-02.png 480w,
/static/1c324fae1c172e57d7bfdc6dd2614df2/374ac/inp-02.png 634w&quot;
        sizes=&quot;(max-width: 634px) 100vw, 634px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Good Score Threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Largest Contentful Paint (LCP)&lt;/td&gt;
&lt;td&gt;How fast the main content loads.&lt;/td&gt;
&lt;td&gt;≤ 2.5 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cumulative Layout Shift (CLS)&lt;/td&gt;
&lt;td&gt;How stable the page layout is.&lt;/td&gt;
&lt;td&gt;≤ 0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interaction to Next Paint (INP)&lt;/td&gt;
&lt;td&gt;How quickly the page responds to user actions.&lt;/td&gt;
&lt;td&gt;≤ 200 milliseconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These metrics help ensure a good user experience by focusing on loading speed, visual stability, and responsiveness.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/courses/web-perf&quot;&gt;Web Performance Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.debugbear.com/docs/metrics/interaction-to-next-paint&quot;&gt;DebugBear: Interaction to Next Paint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.debugbear.com/docs/metrics/largest-contentful-paint&quot;&gt;DebugBear: Largest Contentful Paint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://requestmetrics.com/web-performance/measure-web-performance/&quot;&gt;Measure Web Performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Upgrading from Angular 12 to 15 in Nx Workspace: A Comprehensive Guide]]></title><link>https://trungvose.comnx-angular-15-migration/</link><guid isPermaLink="false">https://trungvose.comnx-angular-15-migration/</guid><pubDate>Sun, 23 Apr 2023 09:32:00 GMT</pubDate><content:encoded>&lt;p&gt;I am currently preparing for a talk at &lt;a href=&quot;https://webdirections.org/code/speakers/trung-vo.php&quot;&gt;Web Directions Code 2023&lt;/a&gt; about CSS container queries.&lt;/p&gt;
&lt;p&gt;However, I have encountered an issue with CSS containers in Angular 12, and it seems that upgrading to Angular 15 is necessary to resolve the issue.&lt;/p&gt;
&lt;p&gt;The problem is related to the &lt;code class=&quot;language-text&quot;&gt;@container&lt;/code&gt; style not being properly emulated into the component. This issue has been causing some difficulties in my preparation for the talk.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/angular/angular/issues/48264&quot;&gt;https://github.com/angular/angular/issues/48264&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-disaster-with-nx-migrate-latest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-disaster-with-nx-migrate-latest&quot; aria-label=&quot;the disaster with nx migrate latest permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The disaster with &lt;code class=&quot;language-text&quot;&gt;nx migrate latest&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;angular-spotify&lt;/code&gt; is using &lt;code class=&quot;language-text&quot;&gt;nrwl@12.5.2&lt;/code&gt; and the latest version is &lt;code class=&quot;language-text&quot;&gt;15.2.8&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I straight away try running &lt;code class=&quot;language-text&quot;&gt;nx migrate latest&lt;/code&gt; to update to the latest version&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; nx migrate latest
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;
v12.22.12

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; nvm use &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
Now using &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; v16.15.1 &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;npm v8.11.0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; nx migrate latest

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; nx migrate --run-migrations

NX   Running &lt;span class=&quot;token string&quot;&gt;&apos;npm install&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; WARN old lockfile
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; WARN old lockfile The package-lock.json &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; was created with an old version of npm,
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; WARN old lockfile so supplemental metadata must be fetched from the registry.
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; WARN old lockfile
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; WARN old lockfile This is a one-time fix-up, please be patient&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; WARN old lockfile&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4030162146efc9949287f67873bb432c/d85b1/nx-angular-15-migration-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABzKtglDAf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAERAhAy/9oACAEBAAEFAkqoQw5ev//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/Aar/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAXEAEAAwAAAAAAAAAAAAAAAAAQMVGB/9oACAEBAAE/Ibg6CR//2gAMAwEAAgADAAAAEBcv/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAFREf/aAAgBAwEBPxBJDEP/xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQIBAT8QW1v/xAAaEAADAQADAAAAAAAAAAAAAAAAAREhEDFx/9oACAEBAAE/EE1erMHDmlDq9EmOP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        title=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        src=&quot;/static/4030162146efc9949287f67873bb432c/6a068/nx-angular-15-migration-01.jpg&quot;
        srcset=&quot;/static/4030162146efc9949287f67873bb432c/09b79/nx-angular-15-migration-01.jpg 240w,
/static/4030162146efc9949287f67873bb432c/7cc5e/nx-angular-15-migration-01.jpg 480w,
/static/4030162146efc9949287f67873bb432c/6a068/nx-angular-15-migration-01.jpg 960w,
/static/4030162146efc9949287f67873bb432c/644c5/nx-angular-15-migration-01.jpg 1440w,
/static/4030162146efc9949287f67873bb432c/0f98f/nx-angular-15-migration-01.jpg 1920w,
/static/4030162146efc9949287f67873bb432c/d85b1/nx-angular-15-migration-01.jpg 2242w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Despite my initial optimism, I encountered numerous issues when attempting to execute &lt;code class=&quot;language-text&quot;&gt;nx migrate --run-migrations&lt;/code&gt;. These issues proved to be quite challenging to resolve, and included errors such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Failed to parse npm lockfile&lt;/li&gt;
&lt;li&gt;Failed to run update-tsconfig-target from &lt;code class=&quot;language-text&quot;&gt;@nrwl/angular&lt;/code&gt;. This workspace is NOT up to date!&lt;/li&gt;
&lt;li&gt;Failed to run update-platform-server-exports from &lt;code class=&quot;language-text&quot;&gt;@nrwl/angular&lt;/code&gt;. This workspace is NOT up to date!&lt;/li&gt;
&lt;li&gt;‘rule’ is not a function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite my best efforts, these issues proved to be impossible to fix. This experience highlights the importance of thoroughly testing and preparing for major upgrades, as even seemingly minor issues can quickly snowball into major problems.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/47df397af3244353e01931fe73953056/fe90e/nx-angular-15-migration-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHlRIMq/8QAFRABAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQEAAQUCa//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABcQAQEBAQAAAAAAAAAAAAAAABEAASD/2gAIAQEAAT8hMIyOH//aAAwDAQACAAMAAAAQfM//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCH/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qp//EABoQAAMBAQEBAAAAAAAAAAAAAAABETEhUaH/2gAIAQEAAT8Q4K3Z4cs+DS6XJFJ4N3R6f//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        title=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        src=&quot;/static/47df397af3244353e01931fe73953056/6a068/nx-angular-15-migration-02.jpg&quot;
        srcset=&quot;/static/47df397af3244353e01931fe73953056/09b79/nx-angular-15-migration-02.jpg 240w,
/static/47df397af3244353e01931fe73953056/7cc5e/nx-angular-15-migration-02.jpg 480w,
/static/47df397af3244353e01931fe73953056/6a068/nx-angular-15-migration-02.jpg 960w,
/static/47df397af3244353e01931fe73953056/644c5/nx-angular-15-migration-02.jpg 1440w,
/static/47df397af3244353e01931fe73953056/0f98f/nx-angular-15-migration-02.jpg 1920w,
/static/47df397af3244353e01931fe73953056/fe90e/nx-angular-15-migration-02.jpg 2420w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/57098c6c9e13d8134f34c558ae0e4120/69ede/nx-angular-15-migration-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByoAD/8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQABBQJ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAQABPyHRz//aAAwDAQACAAMAAAAQ8A//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAACAgMAAAAAAAAAAAAAAAAAARGhIUFR/9oACAEBAAE/EHHQybmaMdo//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        title=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        src=&quot;/static/57098c6c9e13d8134f34c558ae0e4120/6a068/nx-angular-15-migration-03.jpg&quot;
        srcset=&quot;/static/57098c6c9e13d8134f34c558ae0e4120/09b79/nx-angular-15-migration-03.jpg 240w,
/static/57098c6c9e13d8134f34c558ae0e4120/7cc5e/nx-angular-15-migration-03.jpg 480w,
/static/57098c6c9e13d8134f34c558ae0e4120/6a068/nx-angular-15-migration-03.jpg 960w,
/static/57098c6c9e13d8134f34c558ae0e4120/644c5/nx-angular-15-migration-03.jpg 1440w,
/static/57098c6c9e13d8134f34c558ae0e4120/69ede/nx-angular-15-migration-03.jpg 1864w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To avoid further complications, I decided to break down the upgrade process into &lt;code class=&quot;language-text&quot;&gt;multiple steps&lt;/code&gt;. This approach not only helps me to avoid errors but also allows me to experiment with the codebase more effectively.&lt;/p&gt;
&lt;p&gt;While I had initially planned to take this approach, the issues I encountered reinforced the importance of taking a cautious and methodical approach to upgrading.&lt;/p&gt;
&lt;h2 id=&quot;proper-migration-strategy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#proper-migration-strategy&quot; aria-label=&quot;proper migration strategy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Proper migration strategy&lt;/h2&gt;
&lt;p&gt;Thus I decided to do the migration in multiple steps. How? I refer to this &lt;a href=&quot;https://nx.dev/packages/angular/documents/angular-nx-version-matrix&quot;&gt;Nx and Angular Version Compatibility Matrix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/736060cfc02863d8dc4d9b61a57d71bc/cf91a/nx-angular-15-migration-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.83333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHGRqAf/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBAAAwEBAAAAAAAAAAAAAAAAABBBUQH/2gAIAQEAAT8h05VpF//aAAwDAQACAAMAAAAQ+C//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAACAwEBAAAAAAAAAAAAAAAAAREhQRAx/9oACAEBAAE/EJcWxDVLR+0SE7c//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        title=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        src=&quot;/static/736060cfc02863d8dc4d9b61a57d71bc/6a068/nx-angular-15-migration-04.jpg&quot;
        srcset=&quot;/static/736060cfc02863d8dc4d9b61a57d71bc/09b79/nx-angular-15-migration-04.jpg 240w,
/static/736060cfc02863d8dc4d9b61a57d71bc/7cc5e/nx-angular-15-migration-04.jpg 480w,
/static/736060cfc02863d8dc4d9b61a57d71bc/6a068/nx-angular-15-migration-04.jpg 960w,
/static/736060cfc02863d8dc4d9b61a57d71bc/644c5/nx-angular-15-migration-04.jpg 1440w,
/static/736060cfc02863d8dc4d9b61a57d71bc/cf91a/nx-angular-15-migration-04.jpg 1838w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Therefore, I’ve made the decision to migrate from Angular &lt;code class=&quot;language-text&quot;&gt;12&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;13&lt;/code&gt; first, then to &lt;code class=&quot;language-text&quot;&gt;14&lt;/code&gt;, and finally to &lt;code class=&quot;language-text&quot;&gt;15&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In line with that, I’ll also be upgrading Nx from &lt;code class=&quot;language-text&quot;&gt;12.5.2&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;14.1.9&lt;/code&gt; (which supports Angular &lt;code class=&quot;language-text&quot;&gt;13&lt;/code&gt;), and then to &lt;code class=&quot;language-text&quot;&gt;15.2.0&lt;/code&gt;, and finally to the latest version.&lt;/p&gt;
&lt;p&gt;It’s worth mentioning that I skipped Nx 13 since it doesn’t yet support Angular 13, and everything is still functioning smoothly.&lt;/p&gt;
&lt;h3 id=&quot;1-nx-migrate-1419-support-angular-13&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-nx-migrate-1419-support-angular-13&quot; aria-label=&quot;1 nx migrate 1419 support angular 13 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. nx migrate 14.1.9 (support Angular 13)&lt;/h3&gt;
&lt;details&gt;
  &lt;summary&gt;See a full log of nx migrate 14.1.9&lt;/summary&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; nx migrate &lt;span class=&quot;token number&quot;&gt;14.1&lt;/span&gt;.9

Fetching meta data about packages.
It may take a few minutes.
Fetching nx@14.1.9
Fetching @nrwl/workspace@14.1.9
Fetching @nrwl/cypress@14.1.9
Fetching @nrwl/angular@14.1.9
Fetching @nrwl/jest@14.1.9
Fetching @nrwl/linter@14.1.9
Fetching @angular/cli@13.3.11
Fetching @ngrx/store-devtools@13.0.2
Fetching @ngrx/store@13.0.2
Fetching @angular/core@13.3.12
Fetching @angular/cli@~13.3.0
Fetching @ngrx/component-store@13.0.2
Fetching @ngrx/effects@13.0.2
Fetching @ngrx/component@13.0.2

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX   The migrate &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; has run successfully.

- package.json has been updated.
- migrations.json has been generated.

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX   Next steps:

- Make sure package.json changes &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sense and &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; run &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt;,
- Run &lt;span class=&quot;token string&quot;&gt;&apos;yarn nx migrate --run-migrations&apos;&lt;/span&gt;
- To learn &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; go to https://nx.dev/core-features/automate-updating-dependencies&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations --create-commits

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX  Running migrations from &lt;span class=&quot;token string&quot;&gt;&apos;migrations.json&apos;&lt;/span&gt;, with each applied &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; a dedicated commit

Running migration &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;-0-6-remove-root
Successfully finished &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;-0-6-remove-root
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: fdbbae302748b868e78e81f04c973fd8c8317867
---------------------------------------------------------
Running migration update-invalid-import-paths
Successfully finished update-invalid-import-paths
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 972b8d10118b898753592ffe3fc2c98b023ff56d
---------------------------------------------------------
Running migration add-postcss-packages
Successfully finished add-postcss-packages
- There were no changes to commit
---------------------------------------------------------
Running migration update-angular-config
Successfully finished update-angular-config
- There were no changes to commit
---------------------------------------------------------
Running migration update-libraries
Successfully finished update-libraries
- There were no changes to commit
---------------------------------------------------------
Running migration update-angular-jest-config
Successfully finished update-angular-jest-config
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 325354d83ff4260a87b28e2175558d8706bf1eac
---------------------------------------------------------
Running migration update-testing-imports
Successfully finished update-testing-imports
- There were no changes to commit
---------------------------------------------------------
Running migration opt-out-testbed-teardown
Successfully finished opt-out-testbed-teardown
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 29ae169b5246d60fc6446e511d79b23741f750bd
---------------------------------------------------------
Running migration update-mfe-config-to-module-syntax
Successfully finished update-mfe-config-to-module-syntax
- There were no changes to commit
---------------------------------------------------------
Running migration remove-library-generator-style-default
Successfully finished remove-library-generator-style-default
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 25c8f450bfd6e0ddc9e6199feef4bf0a04c00d52
---------------------------------------------------------
Running migration fix-incorrect-mfe-setups
Successfully finished fix-incorrect-mfe-setups
- There were no changes to commit
---------------------------------------------------------
Running migration add-cypress-mfe-workaround
Successfully finished add-cypress-mfe-workaround
- There were no changes to commit
---------------------------------------------------------
Running migration migrate-karma-config
Successfully finished migrate-karma-config
- There were no changes to commit
---------------------------------------------------------
Running migration set-build-libs-from-source
Successfully finished set-build-libs-from-source
- There were no changes to commit
---------------------------------------------------------
Running migration rename-module-federation-config
Successfully finished rename-module-federation-config
- There were no changes to commit
---------------------------------------------------------
Running migration set-default-base-if-not-set
Successfully finished set-default-base-if-not-set
- There were no changes to commit
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-0-0-config-locations
Successfully finished &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-0-0-config-locations
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 066d71ef0bb40fe809b202e9c29ba39344e8ae6e
---------------------------------------------------------
Running migration set-parallel-default
Successfully finished set-parallel-default
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: f897090e59eee9c4ec5770b8fc26ac354a5c52ff
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-3-0-tsc-location
Successfully finished &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-3-0-tsc-location
- There were no changes to commit
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-6-0-remove-old-task-runner-options
Successfully finished &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-6-0-remove-old-task-runner-options
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: c5826fc8c18e1589427a51995a19a629da87989e
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-9-0-replace-tao-with-nx
Please run &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; to ensure the correct version of Nx is installed.
Successfully finished &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-9-0-replace-tao-with-nx
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 4a240af6dbdd5aba2bb1c8048c2f6b01614ac496
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-10-0-update-decorate-cli
Successfully finished &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-10-0-update-decorate-cli
- There were no changes to commit
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-10-0-update-tasks-runner
Successfully finished &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;-10-0-update-tasks-runner
- There were no changes to commit
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;-0-0-change-nx-json-presets
Successfully finished &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;-0-0-change-nx-json-presets
- There were no changes to commit
---------------------------------------------------------
Running migration &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;-0-0-change-npm-script-executor
Successfully finished &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;-0-0-change-npm-script-executor
- There were no changes to commit
---------------------------------------------------------
Running migration remove-typescript-plugin
Successfully finished remove-typescript-plugin
- There were no changes to commit
---------------------------------------------------------
Running migration add-outputs
Successfully finished add-outputs
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 2686c67a14f1440aa37a0f43f76d2ed790d1fbf0
---------------------------------------------------------
Running migration remove-eslint-project-config-if-no-type-checking-rules-again
Successfully finished remove-eslint-project-config-if-no-type-checking-rules-again
- There were no changes to commit
---------------------------------------------------------
Running migration eslint-8-updates
Successfully finished eslint-8-updates
- There were no changes to commit
---------------------------------------------------------
Running migration update-jest-config-to-use-util
Successfully finished update-jest-config-to-use-util
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 3de59c7be81310dba231ac74023fb9e5b934d03e
---------------------------------------------------------
Running migration update-ts-config-for-test-filenames
Unable to update libs/web/shared/data-access/spotify-api/tsconfig.lib.json.
Successfully finished update-ts-config-for-test-filenames
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 432f9e8d855c3e5779e6aa5956e70beabe9a6f49
---------------------------------------------------------
Running migration add-missing-root-babel-config
Successfully finished add-missing-root-babel-config
- There were no changes to commit
---------------------------------------------------------
Running migration update-jest-config-extensions
Nx Unable to update libs/web/shared/data-access/spotify-api/tsconfig.lib.json. Please manually ignore the jest.config.ts file.
Successfully finished update-jest-config-extensions
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 9db789c3a98a5e48479800bc39c7b755e2f507f9
---------------------------------------------------------
Running migration update-to-export-default
Successfully finished update-to-export-default
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 601886d11498f56daa90dbc56f0d3e4eed604e00
---------------------------------------------------------
Running migration ngrx-store-migration-13-beta
Successfully finished ngrx-store-migration-13-beta
- There were no changes to commit
---------------------------------------------------------
Running migration ngrx-store-migration-13-rc
Successfully finished ngrx-store-migration-13-rc
- There were no changes to commit
---------------------------------------------------------
Running migration migration-v13-router-link-empty-expression
Successfully finished migration-v13-router-link-empty-expression
- There were no changes to commit
---------------------------------------------------------
Running migration migration-v13-testbed-teardown
Successfully finished migration-v13-testbed-teardown
- There were no changes to commit
---------------------------------------------------------
Running migration migration-v13.1-entry-components
Successfully finished migration-v13.1-entry-components
- There were no changes to commit
---------------------------------------------------------
Running migration ngrx-effects-migration-03
Successfully finished ngrx-effects-migration-03
- There were no changes to commit
---------------------------------------------------------
Running migration schematic-options-13
Successfully finished schematic-options-13
- There were no changes to commit
---------------------------------------------------------
Running migration update-angular-config-v13
Successfully finished update-angular-config-v13
- There were no changes to commit
---------------------------------------------------------
Running migration update-libraries-v13
Successfully finished update-libraries-v13
- There were no changes to commit
---------------------------------------------------------
Running migration drop-ie-polyfills
Successfully finished drop-ie-polyfills
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: fd4ec7906196569dc3bcbfa1fb8f94ff80ec6d2b
---------------------------------------------------------
Running migration update-gitignore
Successfully finished update-gitignore
- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 221fa6348b88cb29ee4bf793cfb7e1b1bffeb795
---------------------------------------------------------

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  NX  Successfully finished running migrations from &lt;span class=&quot;token string&quot;&gt;&apos;migrations.json&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&quot;2-nx-migrate-1520-support-angular-14-and-above&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-nx-migrate-1520-support-angular-14-and-above&quot; aria-label=&quot;2 nx migrate 1520 support angular 14 and above permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. nx migrate 15.2.0 (support Angular 14 and above)&lt;/h3&gt;
&lt;details&gt;
  &lt;summary&gt;See a full log of nx migrate 15.2.0&lt;/summary&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; nx migrate &lt;span class=&quot;token number&quot;&gt;15.2&lt;/span&gt;.0

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX ✅ This workspace is already connected to Nx Cloud.

This means your workspace can use computation caching, distributed task execution, and show you run analytics.
Go to https://nx.app to learn more.
trung.vo@trung  ~/Source/angular-spotify   trung/angular-15 ±  &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations --create-commits
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
$ nx migrate --run-migrations --create-commits

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍 Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🚚 Fetching packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔗 Linking dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngrx/component@14.0.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^14.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngrx/component@14.0.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^14.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngrx/component-store@14.0.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^14.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngrx/effects@14.0.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^14.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @ngrx/store@14.0.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^14.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;@nrwl/angular &gt; @nrwl/webpack &gt; babel-loader@8.3.0&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@babel/core@^7.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/animations@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/forms@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.0 || ^12.0.0-0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.0 || ^12.0.0-0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.5&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.5&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^11.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔨 Building fresh packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
success Saved lockfile.
$ &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; ./decorate-angular-cli.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ngcc &lt;span class=&quot;token parameter variable&quot;&gt;--properties&lt;/span&gt; es2015 browser module main &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Angular CLI has been decorated to &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; computation caching.
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; husky - Git hooks installed&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; nx migrate --run-migrations --create-commits

&gt; NX Running migrations from &apos;migrations.json&apos;, with each applied in a dedicated commit

Ran 14-2-0-add-json-schema from nx
Add JSON Schema to Nx configuration files

UPDATE nx.json
UPDATE angular.json

- Commit created for changes: 8eda85987526ebbb0253a9021f51015fd00fd36a

---

Ran 14-2-0-remove-default-collection from nx
Remove default collection from configuration to switch to prompts for collection

UPDATE nx.json

- Commit created for changes: 8a5d68b4b19bf1a0f4a3856194bec6c491d13058

---

Ran 14.3.4-create-target-defaults from nx
Replace targetDependencies with targetDefaults

UPDATE nx.json

- Commit created for changes: 348d85e4845308d3bc5539532c7d7ce4711a71bc

---

Ran 15.0.0-migrate-to-inputs from nx
Replace implicitDependencies with namedInputs + target inputs

UPDATE nx.json

- Commit created for changes: 026753402d445ba9f45bd6e3b6dee6afdc72b368

---

Ran 15.0.0-prefix-outputs from nx
Prefix outputs with {workspaceRoot}/{projectRoot} if needed

UPDATE angular.json

- Commit created for changes: 5914df2633fb6194c7014030162f61974cca0361

---

Ran 15.1.0-set-project-names from nx
Set project names in project.json files

UPDATE apps/angular-spotify/project.json
UPDATE libs/web/album/data-access/project.json
UPDATE libs/web/artist/data-access/project.json
UPDATE libs/web/artist/feature/project.json
UPDATE libs/web/auth/data-access/project.json
UPDATE libs/web/auth/util/project.json
UPDATE libs/web/browse/data-access/project.json
UPDATE libs/web/home/data-access/project.json
UPDATE libs/web/home/feature/project.json
UPDATE libs/web/playlist/data-access/project.json
UPDATE libs/web/search/data-access/project.json
UPDATE libs/web/search/feature/project.json
UPDATE libs/web/settings/data-access/project.json
UPDATE libs/web/settings/feature/project.json
UPDATE libs/web/shared/app-config/project.json
UPDATE libs/web/shared/assets/project.json
UPDATE libs/web/shared/utils/project.json
UPDATE libs/web/shell/feature/project.json
UPDATE libs/web/tracks/data-access/project.json
UPDATE libs/web/tracks/feature/project.json
UPDATE libs/web/visualizer/data-access/project.json
UPDATE libs/web/visualizer/feature/project.json
UPDATE libs/web/visualizer/ui/project.json
UPDATE libs/web/album/feature/detail/project.json
UPDATE libs/web/album/feature/list/project.json
UPDATE libs/web/album/feature/shell/project.json
UPDATE libs/web/album/ui/album-track/project.json
UPDATE libs/web/artist/ui/artist-top-track/project.json
UPDATE libs/web/artist/ui/artist-top-tracks/project.json
UPDATE libs/web/auth/ui/unauthorized-modal/project.json
UPDATE libs/web/browse/feature/categories/project.json
UPDATE libs/web/browse/feature/category/project.json
UPDATE libs/web/browse/feature/shell/project.json
UPDATE libs/web/browse/ui/category-cover/project.json
UPDATE libs/web/home/ui/featured-playlists/project.json
UPDATE libs/web/home/ui/greeting/project.json
UPDATE libs/web/home/ui/recent-played/project.json
UPDATE libs/web/playlist/feature/detail/project.json
UPDATE libs/web/playlist/feature/list/project.json
UPDATE libs/web/playlist/ui/playlist-track/project.json
UPDATE libs/web/shared/data-access/models/project.json
UPDATE libs/web/shared/data-access/spotify-api/project.json
UPDATE libs/web/shared/data-access/store/project.json
UPDATE libs/web/shared/directives/click-stop-propagation/project.json
UPDATE libs/web/shared/pipes/duration-pipe/project.json
UPDATE libs/web/shared/ui/icon/project.json
UPDATE libs/web/shared/ui/input/project.json
UPDATE libs/web/shared/ui/media/project.json
UPDATE libs/web/shared/ui/media-cover/project.json
UPDATE libs/web/shared/ui/media-order/project.json
UPDATE libs/web/shared/ui/media-summary/project.json
UPDATE libs/web/shared/ui/media-table/project.json
UPDATE libs/web/shared/ui/play-button/project.json
UPDATE libs/web/shared/ui/playlist-list/project.json
UPDATE libs/web/shared/ui/spinner/project.json
UPDATE libs/web/shared/ui/track-current-info/project.json
UPDATE libs/web/shared/ui/track-main-info/project.json
UPDATE libs/web/shared/ui/tracks-loading/project.json
UPDATE libs/web/shared/ui/work-in-progress/project.json
UPDATE libs/web/shell/ui/album-art-overlay/project.json
UPDATE libs/web/shell/ui/layout/project.json
UPDATE libs/web/shell/ui/main-view/project.json
UPDATE libs/web/shell/ui/nav-bar/project.json
UPDATE libs/web/shell/ui/nav-bar-playlist/project.json
UPDATE libs/web/shell/ui/now-playing-bar/project.json
UPDATE libs/web/shell/ui/player-controls/project.json
UPDATE libs/web/shell/ui/player-playback/project.json
UPDATE libs/web/shell/ui/player-volume/project.json
UPDATE libs/web/shell/ui/social-share/project.json
UPDATE libs/web/shell/ui/top-bar/project.json
UPDATE libs/web/shell/ui/user-dropdown/project.json
UPDATE libs/web/shell/ui/visualization-toggle/project.json

- Commit created for changes: 4d4c04edcbe6da9a56b35df97df53e2fc313bbe9

---

Ran exclude-jest-config-from-ts-config from @nrwl/jest
Exclude jest.config.ts from tsconfig where missing.

UPDATE libs/web/shared/data-access/spotify-api/tsconfig.lib.json

- Commit created for changes: 504ac808bbcd7a447a5b24481ee91f1dfe61b299

---

Ran update-configs-jest-28 from @nrwl/jest
Update jest configs to support jest 28 changes (https://jestjs.io/docs/upgrading-to-jest28#configuration-options)

UPDATE package.json

- Commit created for changes: b5de988aaad4016d7aa72e8f3d58643869868df6

---

Ran add-jest-inputs from @nrwl/jest
Stop hashing jest spec files and config files for build targets and dependent tasks

UPDATE nx.json

- Commit created for changes: 78aa51c9e9728697ac1e797d4351632efa1a7748

---

Ran update-angular-cli-version from @nrwl/angular
Update the @angular/cli package version.

UPDATE package.json

- Commit created for changes: f5fc0ec57aec1dde36398cbcf7515ae958602fe6

---

Ran update-postinstall-script-ngcc-target from @nrwl/angular
Update postinstall script running ngcc to use ES2020 target.

UPDATE package.json

- Commit created for changes: 8af412b8dd9e3c9c82baaa23560b748ee11b22c3

---

Ran update-tsconfig-target from @nrwl/angular
Update TypeScript compilation target to &apos;ES2020&apos;.

UPDATE libs/web/shared/directives/click-stop-propagation/tsconfig.json
UPDATE libs/web/shared/data-access/spotify-api/tsconfig.json
UPDATE libs/web/shell/ui/visualization-toggle/tsconfig.json
UPDATE libs/web/shared/ui/track-current-info/tsconfig.json
UPDATE libs/web/artist/ui/artist-top-tracks/tsconfig.json
UPDATE libs/web/artist/ui/artist-top-track/tsconfig.json
UPDATE libs/web/auth/ui/unauthorized-modal/tsconfig.json
UPDATE libs/web/home/ui/featured-playlists/tsconfig.json
UPDATE libs/web/playlist/ui/playlist-track/tsconfig.json
UPDATE libs/web/shared/pipes/duration-pipe/tsconfig.json
UPDATE libs/web/shared/ui/work-in-progress/tsconfig.json
UPDATE libs/web/shell/ui/album-art-overlay/tsconfig.json
UPDATE libs/web/browse/feature/categories/tsconfig.json
UPDATE libs/web/shared/ui/track-main-info/tsconfig.json
UPDATE libs/web/shell/ui/nav-bar-playlist/tsconfig.json
UPDATE libs/web/browse/ui/category-cover/tsconfig.json
UPDATE libs/web/shared/data-access/store/tsconfig.json
UPDATE libs/web/shared/ui/tracks-loading/tsconfig.json
UPDATE libs/web/shell/ui/now-playing-bar/tsconfig.json
UPDATE libs/web/shell/ui/player-controls/tsconfig.json
UPDATE libs/web/shell/ui/player-playback/tsconfig.json
UPDATE libs/web/browse/feature/category/tsconfig.json
UPDATE libs/web/playlist/feature/detail/tsconfig.json
UPDATE libs/web/shared/ui/media-summary/tsconfig.json
UPDATE libs/web/shared/ui/playlist-list/tsconfig.json
UPDATE libs/web/shell/ui/player-volume/tsconfig.json
UPDATE libs/web/shell/ui/user-dropdown/tsconfig.json
UPDATE libs/web/visualizer/data-access/tsconfig.json
UPDATE libs/web/home/ui/recent-played/tsconfig.json
UPDATE libs/web/playlist/feature/list/tsconfig.json
UPDATE libs/web/shared/ui/media-cover/tsconfig.json
UPDATE libs/web/shared/ui/media-order/tsconfig.json
UPDATE libs/web/shared/ui/media-table/tsconfig.json
UPDATE libs/web/shared/ui/play-button/tsconfig.json
UPDATE libs/web/shell/ui/social-share/tsconfig.json
UPDATE libs/web/album/feature/detail/tsconfig.json
UPDATE libs/web/album/ui/album-track/tsconfig.json
UPDATE libs/web/browse/feature/shell/tsconfig.json
UPDATE libs/web/playlist/data-access/tsconfig.json
UPDATE libs/web/settings/data-access/tsconfig.json
UPDATE libs/web/album/feature/shell/tsconfig.json
UPDATE libs/web/album/feature/list/tsconfig.json
UPDATE libs/web/artist/data-access/tsconfig.json
UPDATE libs/web/browse/data-access/tsconfig.json
UPDATE libs/web/search/data-access/tsconfig.json
UPDATE libs/web/shell/ui/main-view/tsconfig.json
UPDATE libs/web/tracks/data-access/tsconfig.json
UPDATE libs/web/visualizer/feature/tsconfig.json
UPDATE libs/web/album/data-access/tsconfig.json
UPDATE libs/web/shared/app-config/tsconfig.json
UPDATE libs/web/shared/ui/spinner/tsconfig.json
UPDATE libs/web/auth/data-access/tsconfig.json
UPDATE libs/web/home/data-access/tsconfig.json
UPDATE libs/web/home/ui/greeting/tsconfig.json
UPDATE libs/web/settings/feature/tsconfig.json
UPDATE libs/web/shell/ui/nav-bar/tsconfig.json
UPDATE libs/web/shell/ui/top-bar/tsconfig.json
UPDATE libs/web/shared/app-init/tsconfig.json
UPDATE libs/web/shared/ui/input/tsconfig.json
UPDATE libs/web/shared/ui/media/tsconfig.json
UPDATE libs/web/shell/ui/layout/tsconfig.json
UPDATE libs/web/artist/feature/tsconfig.json
UPDATE libs/web/search/feature/tsconfig.json
UPDATE libs/web/shared/ui/icon/tsconfig.json
UPDATE libs/web/tracks/feature/tsconfig.json
UPDATE libs/web/shell/feature/tsconfig.json
UPDATE libs/web/visualizer/ui/tsconfig.json
UPDATE libs/web/home/feature/tsconfig.json
UPDATE apps/angular-spotify/tsconfig.json
UPDATE libs/web/auth/util/tsconfig.json

- Commit created for changes: 81f0a5670a0fdd9f7131e5dbca1830154e54b64a

---

Ran update-angular-cli-version-14-1-0 from @nrwl/angular
Update the @angular/cli package version to ~14.1.0.

UPDATE package.json

- Commit created for changes: 9d39a2e9eb26fae063e8e34bba4ecbc4ff49904c

---

Ran update-angular-cli-version-14-2-0 from @nrwl/angular
Update the @angular/cli package version to ~14.2.0.

UPDATE package.json

- Commit created for changes: 345ec3a60645f7837c0d1c215f02335a1a064804

---

Ran update-angular-cli-version-15-0-0 from @nrwl/angular
Update the @angular/cli package version to ~15.0.0.

UPDATE package.json

- Commit created for changes: 2ae14b1641e043c0d0540a1262bc79a3dd500880

---

Ran remove-browserlist-config from @nrwl/angular
Remove browserlist config as it&apos;s handled by build-angular

DELETE apps/angular-spotify/.browserslistrc

- Commit created for changes: 6e48af0215db5859886e48c2d2a7fa7a76fc4a1c

---

Ran update-typescript-target from @nrwl/angular
Update typescript target to ES2022

UPDATE apps/angular-spotify/tsconfig.app.json

- Commit created for changes: 2091c12a721c9e5c1a46fb5c67153e62a222d5e3

---

[NX] Angular devkit readJsonWorkspace fell back to Nx workspaces logic
Project &apos;web-shared-directives-click-stop-propagation&apos; contains extension with invalid name (files).
Project &apos;web-shared-data-access-spotify-api&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-visualization-toggle&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-track-current-info&apos; contains extension with invalid name (files).
Project &apos;web-artist-ui-artist-top-tracks&apos; contains extension with invalid name (files).
Project &apos;web-artist-ui-artist-top-track&apos; contains extension with invalid name (files).
Project &apos;web-auth-ui-unauthorized-modal&apos; contains extension with invalid name (files).
Project &apos;web-home-ui-featured-playlists&apos; contains extension with invalid name (files).
Project &apos;web-playlist-ui-playlist-track&apos; contains extension with invalid name (files).
Project &apos;web-shared-pipes-duration-pipe&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-work-in-progress&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-album-art-overlay&apos; contains extension with invalid name (files).
Project &apos;web-browse-feature-categories&apos; contains extension with invalid name (files).
Project &apos;web-shared-data-access-models&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-track-main-info&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-nav-bar-playlist&apos; contains extension with invalid name (files).
Project &apos;web-browse-ui-category-cover&apos; contains extension with invalid name (files).
Project &apos;web-shared-data-access-store&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-tracks-loading&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-now-playing-bar&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-player-controls&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-player-playback&apos; contains extension with invalid name (files).
Project &apos;web-browse-feature-category&apos; contains extension with invalid name (files).
Project &apos;web-playlist-feature-detail&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-media-summary&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-playlist-list&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-player-volume&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-user-dropdown&apos; contains extension with invalid name (files).
Project &apos;web-visualizer-data-access&apos; contains extension with invalid name (files).
Project &apos;web-home-ui-recent-played&apos; contains extension with invalid name (files).
Project &apos;web-playlist-feature-list&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-media-cover&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-media-order&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-media-table&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-play-button&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-social-share&apos; contains extension with invalid name (files).
Project &apos;web-album-feature-detail&apos; contains extension with invalid name (files).
Project &apos;web-album-ui-album-track&apos; contains extension with invalid name (files).
Project &apos;web-browse-feature-shell&apos; contains extension with invalid name (files).
Project &apos;web-playlist-data-access&apos; contains extension with invalid name (files).
Project &apos;web-settings-data-access&apos; contains extension with invalid name (files).
Project &apos;web-album-feature-shell&apos; contains extension with invalid name (files).
Project &apos;web-album-feature-list&apos; contains extension with invalid name (files).
Project &apos;web-artist-data-access&apos; contains extension with invalid name (files).
Project &apos;web-browse-data-access&apos; contains extension with invalid name (files).
Project &apos;web-search-data-access&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-main-view&apos; contains extension with invalid name (files).
Project &apos;web-tracks-data-access&apos; contains extension with invalid name (files).
Project &apos;web-visualizer-feature&apos; contains extension with invalid name (files).
Project &apos;web-album-data-access&apos; contains extension with invalid name (files).
Project &apos;web-shared-app-config&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-spinner&apos; contains extension with invalid name (files).
Project &apos;web-auth-data-access&apos; contains extension with invalid name (files).
Project &apos;web-home-data-access&apos; contains extension with invalid name (files).
Project &apos;web-home-ui-greeting&apos; contains extension with invalid name (files).
Project &apos;web-settings-feature&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-nav-bar&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-top-bar&apos; contains extension with invalid name (files).
Project &apos;web-shared-app-init&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-input&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-media&apos; contains extension with invalid name (files).
Project &apos;web-shell-ui-layout&apos; contains extension with invalid name (files).
Project &apos;web-artist-feature&apos; contains extension with invalid name (files).
Project &apos;web-search-feature&apos; contains extension with invalid name (files).
Project &apos;web-shared-ui-icon&apos; contains extension with invalid name (files).
Project &apos;web-tracks-feature&apos; contains extension with invalid name (files).
Project &apos;web-shared-assets&apos; contains extension with invalid name (files).
Project &apos;web-shell-feature&apos; contains extension with invalid name (files).
Project &apos;web-visualizer-ui&apos; contains extension with invalid name (files).
Project &apos;web-home-feature&apos; contains extension with invalid name (files).
Project &apos;web-shared-utils&apos; contains extension with invalid name (files).
Project &apos;angular-spotify&apos; contains extension with invalid name (files).
Project &apos;web-auth-util&apos; contains extension with invalid name (files).
Ran add-eslint-inputs from @nrwl/linter
Stop hashing eslint config files for build targets and dependent tasks

UPDATE nx.json

- Commit created for changes: 6852947b4baea8065775fa34a8976d11ce615bc8

---

&gt; NX Running &apos;yarn&apos; to make sure necessary packages are installed

[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
warning &quot; &gt; @ngrx/component@14.0.2&quot; has incorrect peer dependency &quot;@angular/common@^14.0.0&quot;.
warning &quot; &gt; @ngrx/component@14.0.2&quot; has incorrect peer dependency &quot;@angular/core@^14.0.0&quot;.
warning &quot; &gt; @ngrx/component-store@14.0.2&quot; has incorrect peer dependency &quot;@angular/core@^14.0.0&quot;.
warning &quot; &gt; @ngrx/effects@14.0.2&quot; has incorrect peer dependency &quot;@angular/core@^14.0.0&quot;.
warning &quot; &gt; @ngrx/store@14.0.2&quot; has incorrect peer dependency &quot;@angular/core@^14.0.0&quot;.
warning &quot; &gt; @sentry/angular@6.8.0&quot; has incorrect peer dependency &quot;@angular/common@10.x || 11.x || 12.x&quot;.
warning &quot; &gt; @sentry/angular@6.8.0&quot; has incorrect peer dependency &quot;@angular/core@10.x || 11.x || 12.x&quot;.
warning &quot; &gt; @sentry/angular@6.8.0&quot; has incorrect peer dependency &quot;@angular/router@10.x || 11.x || 12.x&quot;.
warning &quot; &gt; ng-zorro-antd@11.4.2&quot; has incorrect peer dependency &quot;@angular/animations@^11.0.4&quot;.
warning &quot; &gt; ng-zorro-antd@11.4.2&quot; has incorrect peer dependency &quot;@angular/common@^11.0.4&quot;.
warning &quot; &gt; ng-zorro-antd@11.4.2&quot; has incorrect peer dependency &quot;@angular/forms@^11.0.4&quot;.
warning &quot; &gt; ng-zorro-antd@11.4.2&quot; has incorrect peer dependency &quot;@angular/core@^11.0.4&quot;.
warning &quot; &gt; ng-zorro-antd@11.4.2&quot; has incorrect peer dependency &quot;@angular/platform-browser@^11.0.4&quot;.
warning &quot; &gt; ng-zorro-antd@11.4.2&quot; has incorrect peer dependency &quot;@angular/router@^11.0.4&quot;.
warning &quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot; has incorrect peer dependency &quot;@angular/core@^11.0.0 || ^12.0.0-0&quot;.
warning &quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot; has incorrect peer dependency &quot;@angular/common@^11.0.0 || ^12.0.0-0&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot; has incorrect peer dependency &quot;@angular/common@^11.0.5&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot; has incorrect peer dependency &quot;@angular/core@^11.0.5&quot;.
warning &quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot; has incorrect peer dependency &quot;@angular/platform-browser@^11.0.0&quot;.
[4/4] 🔨 Building fresh packages...
success Saved lockfile.
$ node ./decorate-angular-cli.js &amp;amp;&amp;amp; ngcc --properties es2020 browser module main &amp;amp;&amp;amp; husky install

&gt; NX Angular CLI has been decorated to enable computation caching.
  husky - Git hooks installed

&gt; NX Successfully finished running migrations from &apos;migrations.json&apos;. This workspace is up to date!

✨ Done in 174.19s.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;h3 id=&quot;3-nx-migrate-latest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-nx-migrate-latest&quot; aria-label=&quot;3 nx migrate latest permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. nx migrate latest&lt;/h3&gt;
&lt;details&gt;
  &lt;summary&gt;See the full log of nx migrate latest&lt;/summary&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;nx migrate latest
Fetching meta data about packages.
It may take a few minutes.
Fetching nx@15.9.2
Fetching @nrwl/jest@15.9.2
Fetching @nrwl/angular@15.9.2
Fetching @nrwl/cypress@15.9.2
Fetching @nrwl/workspace@15.9.2
Fetching @nrwl/linter@15.9.2
Fetching @angular/core@15.2.8
Fetching @ngrx/effects@15.3.0
Fetching @ngrx/store@15.3.0
Fetching @ngrx/component-store@15.3.0
Fetching @ngrx/store-devtools@15.3.0
Fetching @ngrx/component@15.3.0

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX The migrate &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; has run successfully.

- package.json has been updated.
- migrations.json has been generated.

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Next steps:

- Make sure package.json changes &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sense and &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt; run &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt;,
- Run &lt;span class=&quot;token string&quot;&gt;&apos;yarn nx migrate --run-migrations&apos;&lt;/span&gt;
- To learn &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; go to https://nx.dev/core-features/automate-updating-dependencies
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; nx migrate --run-migrations --create-commits
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; run v1.22.19
&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;$ nx migrate --run-migrations --create-commits

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍 Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🚚 Fetching packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔗 Linking dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @nrwl/angular@15.9.2&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular-devkit/core@&gt;= 14.0.0 &amp;lt; 16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @nrwl/angular@15.9.2&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular-devkit/schematics@&gt;= 14.0.0 &amp;lt; 16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @nrwl/angular@15.9.2&quot;&lt;/span&gt; has unmet peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@schematics/angular@&gt;= 14.0.0 &amp;lt; 16.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/animations@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/forms@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.0 || ^12.0.0-0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.0 || ^12.0.0-0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.5&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.5&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^11.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔨 Building fresh packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
success Saved lockfile.
&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;$ &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; ./decorate-angular-cli.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ngcc &lt;span class=&quot;token parameter variable&quot;&gt;--properties&lt;/span&gt; es2020 browser module main &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Decoration of the Angular CLI is deprecated and will be removed &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; a future version

Please replace usage of &lt;span class=&quot;token string&quot;&gt;&quot;ng &amp;lt;command&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; any scripts, particularly &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; CI, with &lt;span class=&quot;token string&quot;&gt;&quot;nx &amp;lt;command&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Angular CLI has been decorated to &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; computation caching.
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; husky - Git hooks installed
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Ran install-required-packages from @nrwl/angular
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Install the required angular-devkit packages as we &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; not directly depend on them anymore

UPDATE package.json

- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 3c7ac4fed751545f2e56e2c4fee491e1864a4574

---

Ran update-angular-cli-version-15-1-0 from @nrwl/angular
Update the @angular/cli package version to ~15.1.0.

UPDATE package.json

- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: fe732dd4503ecf6f207fbb0f58dd304cd115d79e

---

Ran update-angular-cli-version-15-2-0 from @nrwl/angular
Update the @angular/cli package version to ~15.2.0.

UPDATE package.json

- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: b63ce242f43a39dbd5961495ff5f5d57a824a89c

---

Ran update-tsconfig-spec-jest from @nrwl/angular
Update the tsconfig.spec.json to use target es2016 &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; jest-preset-angular v13

UPDATE libs/web/shared/app-init/tsconfig.spec.json

- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: 6fe21b49efed5169578dc8494fc840a78d04d7f6

---

Ran add-eslint-ignore from @nrwl/linter
Add node_modules to root eslint ignore

CREATE .eslintignore

- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: a83d02d70a963e60aeed5de895c24cd1507b6d14

---

Ran ngrx-component-migration-15-beta from @ngrx/component
As of NgRx v14, &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;ReactiveComponentModule&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt; is deprecated. It is replaced by &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;LetModule&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt; and &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;PushModule&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;

UPDATE libs/web/album/ui/album-track/src/lib/album-track.module.ts
UPDATE libs/web/artist/ui/artist-top-track/src/lib/artist-top-track.module.ts
UPDATE libs/web/browse/feature/category/src/lib/category.module.ts
UPDATE libs/web/playlist/feature/detail/src/lib/playlist.module.ts
UPDATE libs/web/playlist/ui/playlist-track/src/lib/playlist-track.module.ts
UPDATE libs/web/shared/ui/media/src/lib/media.module.ts
UPDATE libs/web/shared/ui/media-order/src/lib/media-order.module.ts
UPDATE libs/web/shared/ui/play-button/src/lib/play-button.module.ts
UPDATE libs/web/shell/ui/nav-bar-playlist/src/lib/nav-bar-playlist.module.ts
UPDATE libs/web/shell/ui/visualization-toggle/src/lib/visualization-toggle.module.ts
UPDATE libs/web/visualizer/ui/src/lib/web-visualizer-ui.module.ts

- Commit created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; changes: dad2a2353699b1c76578cf9b220379b1df569b40

---

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Running &lt;span class=&quot;token string&quot;&gt;&apos;yarn&apos;&lt;/span&gt; to &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt; sure necessary packages are installed

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔍 Resolving packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🚚 Fetching packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔗 Linking dependencies&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; @sentry/angular@6.8.0&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@10.x || 11.x || 12.x&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/animations@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/forms@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot; &gt; ng-zorro-antd@11.4.2&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/router@^11.0.4&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.0 || ^12.0.0-0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @angular/cdk@11.2.13&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.0 || ^12.0.0-0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/common@^11.0.5&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core@^11.0.5&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
warning &lt;span class=&quot;token string&quot;&gt;&quot;ng-zorro-antd &gt; @ant-design/icons-angular@11.0.1&quot;&lt;/span&gt; has incorrect peer dependency &lt;span class=&quot;token string&quot;&gt;&quot;@angular/platform-browser@^11.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;/4&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; 🔨 Building fresh packages&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
success Saved lockfile.
&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;$ &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; ./decorate-angular-cli.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ngcc &lt;span class=&quot;token parameter variable&quot;&gt;--properties&lt;/span&gt; es2020 browser module main &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Decoration of the Angular CLI is deprecated and will be removed &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; a future version

Please replace usage of &lt;span class=&quot;token string&quot;&gt;&quot;ng &amp;lt;command&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; any scripts, particularly &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; CI, with &lt;span class=&quot;token string&quot;&gt;&quot;nx &amp;lt;command&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Angular CLI has been decorated to &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; computation caching.

husky - Git hooks installed

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; NX Successfully finished running migrations from &lt;span class=&quot;token string&quot;&gt;&apos;migrations.json&apos;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; This workspace is up to date&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;&lt;strong&gt;All three steps above are done without any errors&lt;/strong&gt; 🎉🎉🎉. I also tried to run the app with &lt;code class=&quot;language-text&quot;&gt;nx serve&lt;/code&gt; and it works fine.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6e5fd7b6042976118b21a0e037357b9a/39869/nx-angular-15-migration-05.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHkl4hlH//EABcQAAMBAAAAAAAAAAAAAAAAAAABECH/2gAIAQEAAQUCysU//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAEREDFR/9oACAEBAAE/IW+MQ0HjZT//2gAMAwEAAgADAAAAENQ//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QJ//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQACAwEAAAAAAAAAAAAAAAEAESExQXH/2gAIAQEAAT8Q0s3yVYoagkuZYQrBzkRbWrn/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        title=&quot;Migrate from Angular 12 to 15 in Nx&quot;
        src=&quot;/static/6e5fd7b6042976118b21a0e037357b9a/6a068/nx-angular-15-migration-05.jpg&quot;
        srcset=&quot;/static/6e5fd7b6042976118b21a0e037357b9a/09b79/nx-angular-15-migration-05.jpg 240w,
/static/6e5fd7b6042976118b21a0e037357b9a/7cc5e/nx-angular-15-migration-05.jpg 480w,
/static/6e5fd7b6042976118b21a0e037357b9a/6a068/nx-angular-15-migration-05.jpg 960w,
/static/6e5fd7b6042976118b21a0e037357b9a/644c5/nx-angular-15-migration-05.jpg 1440w,
/static/6e5fd7b6042976118b21a0e037357b9a/0f98f/nx-angular-15-migration-05.jpg 1920w,
/static/6e5fd7b6042976118b21a0e037357b9a/39869/nx-angular-15-migration-05.jpg 3404w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[nx:run-commands output not colored]]></title><link>https://trungvose.comnx-commands-output-color/</link><guid isPermaLink="false">https://trungvose.comnx-commands-output-color/</guid><pubDate>Thu, 02 Mar 2023 09:32:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;We are using Nx in our project and recently we want to utlize &lt;code class=&quot;language-text&quot;&gt;nx:run-commands&lt;/code&gt; to run two commands:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;nx serve&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;open&lt;/code&gt; a custom our configured domain&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, we found that the output of the command is not colored.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;serve-open&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;executor&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nx:run-commands&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;options&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;commands&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;nx run my-bank:serve&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;open https://our-custom-domain.local&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;parallel&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;./nx-run-commands-output-not-colored.png&quot;&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA40lEQVQoz5VRRa4FMQzrPYY1zMx0/1P5yZE66uJv/sJKmsQOVPm+jyzLUFUV2rZFkiSwbRuu6/4bnudBdV2Hfd+xbZsIUjxNUxRFgTzPEcfxhyiKJEewjnlagnnG1bquuK4Lx3GAPu08z2CjaZowjqMQWaxJupHp0xKKJAq974v7vj9xxolhGMRyehLLshToCYMgkFU11LIsQuj7XtY01yVJF/LWnDYMw+9N6ziO3K+ua+HJyk3TiBC7WZYln6JBgibxzryjGSfIO89TGio6FNUd//o9LchzcHU2NXMUfJ5HNv0BICbNA/oSD7YAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nx run-commands output not colored&quot;
        title=&quot;nx run-commands output not colored&quot;
        src=&quot;/static/59dcd9dd6b91ab8c5f26df843fda58cd/d9199/nx-run-commands-output-not-colored.png&quot;
        srcset=&quot;/static/59dcd9dd6b91ab8c5f26df843fda58cd/8ff5a/nx-run-commands-output-not-colored.png 240w,
/static/59dcd9dd6b91ab8c5f26df843fda58cd/e85cb/nx-run-commands-output-not-colored.png 480w,
/static/59dcd9dd6b91ab8c5f26df843fda58cd/d9199/nx-run-commands-output-not-colored.png 960w,
/static/59dcd9dd6b91ab8c5f26df843fda58cd/0d98f/nx-run-commands-output-not-colored.png 1276w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;After visiting some related issues, we found that we need to set &lt;code class=&quot;language-text&quot;&gt;--output-style&lt;/code&gt; to whether &lt;code class=&quot;language-text&quot;&gt;stream&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;stream-without-prefixes&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This &lt;a href=&quot;https://github.com/nrwl/nx/issues/10918#issuecomment-1202731648&quot;&gt;10918#issuecomment-1202731648&lt;/a&gt; is the most helpful one.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&quot;serve-open&quot;: {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;executor&quot;: &quot;nx:run-commands&quot;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &quot;options&quot;: {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &quot;commands&quot;: [
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;     &quot;nx run my-bank:serve&quot;,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &quot;nx run my-bank:serve --output-style=stream-without-prefixes&quot;,
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &quot;open https://our-custom-domain.local&quot;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   ],
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &quot;parallel&quot;: true
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; }
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the output looks as expected:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;./nx-run-commands-output-colored.png&quot;&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 37.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABNUlEQVQoz12OV27DMBQEdRCxqZIyJcoqVDEMIz+5/40mEFOQ5GOwC77lYrN1NbxeLa9XxzxbvDfcbpIQNN4rnJN0nfqh71V6HwbNOCpCUCl7u3367DjuvL/vvL1FYpzZ98iyTNzvww9tW1NVBXVd0rZVomlKrK2wtqZtL9+kXHaennXdeDyenOeD5/PJtu3M80yMMemyLAzDQF03eN/jXIe1DuccZVliTEFRlInsPnbEdWXftvTJe0/f94QQkhpjKIprXZ0KLi0K81VkyPMcKQV1VaKkJKviSDN6GmdRRqeAECLpb6+U4jgOtNZ/bhfaGGw8Kf1AJqInnxx5bRBGIYVEyr9cn64153nSdd3Xqu9bji5KxPpA+0AmhxYZLFKIhLiCl/9XeOk0TVhr/xWKtF75gG4tH2tyzB+54MOKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;nx run-commands output colored&quot;
        title=&quot;nx run-commands output colored&quot;
        src=&quot;/static/cc8ce418366ff923191184d9c5559846/d9199/nx-run-commands-output-colored.png&quot;
        srcset=&quot;/static/cc8ce418366ff923191184d9c5559846/8ff5a/nx-run-commands-output-colored.png 240w,
/static/cc8ce418366ff923191184d9c5559846/e85cb/nx-run-commands-output-colored.png 480w,
/static/cc8ce418366ff923191184d9c5559846/d9199/nx-run-commands-output-colored.png 960w,
/static/cc8ce418366ff923191184d9c5559846/21b4d/nx-run-commands-output-colored.png 1280w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Prettier - prevent HTML closing tag > being placed on a new line?]]></title><link>https://trungvose.comprettier-prevent-html-closing-tag-new-line/</link><guid isPermaLink="false">https://trungvose.comprettier-prevent-html-closing-tag-new-line/</guid><pubDate>Wed, 01 Feb 2023 06:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Our project uses Prettier to format our code. We have Angular components that have HTML code in them.&lt;/p&gt;
&lt;p&gt;Prettier formats the HTML code in the Angular component and places the closing tag &lt;code class=&quot;language-text&quot;&gt;&gt;&lt;/code&gt; on a new line. This is not what we want.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mb-10 flex&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    I accept the
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;underline&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;routerLink&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/tnc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Our terms and conditions&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The reason is because Prettier is trying to preserve the whitespace between the &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; tag and the closing tag &lt;code class=&quot;language-text&quot;&gt;&gt;&lt;/code&gt;. Thus, it places text like &lt;code class=&quot;language-text&quot;&gt;&gt;Our terms and conditions&amp;lt;&lt;/code&gt; so that the whitespace is preserved.&lt;/p&gt;
&lt;p&gt;See the explanation from the Prettier documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/prettier/prettier/issues/9381#issuecomment-707156908&quot;&gt;https://github.com/prettier/prettier/issues/9381#issuecomment-707156908&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prettier.io/blog/2018/11/07/1.15.0.html#whitespace-sensitive-formatting&quot;&gt;https://prettier.io/blog/2018/11/07/1.15.0.html#whitespace-sensitive-formatting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;Since we are not interested in preserving the whitespace between HTML tag, we can tell Prettier to ignore the whitespace sensitivity by updating Prettier configuration file with &lt;code class=&quot;language-text&quot;&gt;htmlWhitespaceSensitivity&lt;/code&gt; option to &lt;code class=&quot;language-text&quot;&gt;ignore&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;module.exports = {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; bracketSameLine: true,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; trailingComma: &apos;all&apos;,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; tabWidth: 2,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; semi: true,
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; singleQuote: true,
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;  htmlWhitespaceSensitivity: &apos;ignore&apos;,
&lt;/span&gt;};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run prettier again for all html files, the closing tag &lt;code class=&quot;language-text&quot;&gt;&gt;&lt;/code&gt; should be on the same line as the last attribute.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx prettier &lt;span class=&quot;token string&quot;&gt;&quot;**/*.html&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--write&lt;/span&gt; --ignore-unknown&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mb-10 flex&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    I accept the
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;underline&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;routerLink&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/tnc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      Our terms and conditions
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/63654373&quot;&gt;https://stackoverflow.com/a/63654373&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/64329629&quot;&gt;https://stackoverflow.com/a/64329629&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Perceived Performance: The psychology of waiting]]></title><description><![CDATA[Understand the psychology of waiting and how to make your website feel faster to users.]]></description><link>https://trungvose.comperceived-performance/</link><guid isPermaLink="false">https://trungvose.comperceived-performance/</guid><pubDate>Fri, 13 Jan 2023 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently took the course &lt;a href=&quot;https://frontendmasters.com/courses/web-perf&quot;&gt;Web Performance Fundamentals&lt;/a&gt;, which covered an intriguing topic about perceived performance. This concept revolves around the psychology of waiting and how we can make our websites feel faster to users.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/55e11ff2e5bb1b670e93be496b9103e3/01ab0/perceived-performance-06.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.666666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAECBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAAB56sWGYSX/8QAFxABAQEBAAAAAAAAAAAAAAAAAzIAAv/aAAgBAQABBQIYLJZQe7v/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAWEQEBAQAAAAAAAAAAAAAAAAAAAUH/2gAIAQIBAT8BmK//xAAYEAADAQEAAAAAAAAAAAAAAAAAAXECEP/aAAgBAQAGPwI1R8dGf//EABoQAAIDAQEAAAAAAAAAAAAAAAABESExoXH/2gAIAQEAAT8hhtENKR1jU9Gouw//2gAMAwEAAgADAAAAEJMf/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAExQf/aAAgBAwEBPxB6KH//xAAWEQEBAQAAAAAAAAAAAAAAAAAAATH/2gAIAQIBAT8QGq//xAAbEAACAgMBAAAAAAAAAAAAAAABEQAhEEFRYf/aAAgBAQABPxCigllX7Kth2sAYK6hgnuG5v//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Perceived Performance&quot;
        title=&quot;Perceived Performance&quot;
        src=&quot;/static/55e11ff2e5bb1b670e93be496b9103e3/6a068/perceived-performance-06.jpg&quot;
        srcset=&quot;/static/55e11ff2e5bb1b670e93be496b9103e3/09b79/perceived-performance-06.jpg 240w,
/static/55e11ff2e5bb1b670e93be496b9103e3/7cc5e/perceived-performance-06.jpg 480w,
/static/55e11ff2e5bb1b670e93be496b9103e3/6a068/perceived-performance-06.jpg 960w,
/static/55e11ff2e5bb1b670e93be496b9103e3/01ab0/perceived-performance-06.jpg 1300w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Over the years, I’ve been deeply interested in web performance and how to make websites faster. I’ve used tools like Lighthouse, WebPageTest, and Chrome DevTools to measure and improve my site’s performance. However, I never really considered the perceived performance of my website. I always focused on actual performance metrics like First Contentful Paint (FCP), Largest Contentful Paint (LCP), and Cumulative Layout Shift (CLS). But perceived performance is just as crucial as actual performance. In this article, I’ll share insights from Todd Gardner’s course about the Psychology of Waiting and how we can apply these principles to make our websites feel faster to users.&lt;/p&gt;
&lt;h2 id=&quot;update-2024&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update-2024&quot; aria-label=&quot;update 2024 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update 2024&lt;/h2&gt;
&lt;p&gt;I recently presented a 30-minute talk titled “Creating Fast-Feeling Web Apps” at JSConfJP in Tokyo, one of the largest web developer events in Japan. The feedback from attendees was overwhelmingly positive. You can view the slides and the recorded talk here: &lt;a href=&quot;/talks/2024-11-23-jsconfjp/&quot;&gt;Creating Fast-Feeling Web Apps at JSConfJP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/speaking/2024-11-23-jsconfjp-00.jpg&quot; alt=&quot;JSConfJP 2024&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-psychology-of-waiting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-psychology-of-waiting&quot; aria-label=&quot;the psychology of waiting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Psychology of Waiting&lt;/h2&gt;
&lt;p&gt;Todd Gardner referenced &lt;a href=&quot;https://davidmaister.com/articles/the-psychology-of-waiting-lines/&quot;&gt;The Psychology of Waiting Lines&lt;/a&gt; by David Maister, which outlines eight key points. Here are six of them:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Wait Time is Subjective&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Imagine going to an empty bar. You expect to be served quickly, but if the bartender is on the phone for a minute, it feels like a long wait because you have nothing to do. Conversely, in a busy bar with a bustling scene, the wait feels shorter because you’re engaged.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Bored Waits Feel Slower&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Think about road trips before the era of smartphones and in-car entertainment. Staring out the window made the trip feel endless, even if it was just two hours.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Anxious Waits Feel Slower&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Waiting for high-stress results, like visa approvals or loan decisions, feels much longer than waiting for something trivial. The uncertainty and anxiety make the wait unbearable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Uncertain Waits Feel Slower&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Not knowing how long a wait will be can make it feel interminable. Is it going to be a minute, five minutes, or an hour?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Unexplained Waits Feel Slower&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you don’t understand why you’re waiting, the wait feels longer. For example, if a bartender takes a long time without explanation, it feels frustrating.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. People Will Wait for Value&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;People are willing to wait longer for something valuable. For instance, they’ll wait for a high-quality video to buffer but won’t wait long for a trivial news article.&lt;/p&gt;
&lt;h2 id=&quot;applying-the-psychology-of-waiting-to-web-performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applying-the-psychology-of-waiting-to-web-performance&quot; aria-label=&quot;applying the psychology of waiting to web performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Applying the Psychology of Waiting to Web Performance&lt;/h2&gt;
&lt;p&gt;Understanding these psychological principles can help us design web experiences that feel faster to users, even if the actual load times remain unchanged. However, before we dive into the solutions, let’s see how &lt;strong&gt;long&lt;/strong&gt; is &lt;strong&gt;too long&lt;/strong&gt; for users to wait.&lt;/p&gt;
&lt;p&gt;Many cite &lt;a href=&quot;https://www.nngroup.com/articles/response-times-3-important-limits/&quot;&gt;Jakob Nielsen&lt;/a&gt;, who specifies three limits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.&lt;/li&gt;
&lt;li&gt;1.0 second is about the limit for the user’s flow of thought to stay uninterrupted, even though the user will notice the delay. Normally, no special feedback is necessary during delays of more than 0.1 but less than 1.0 second, but the user does lose the feeling of operating directly on the data.&lt;/li&gt;
&lt;li&gt;10 seconds is about the limit for keeping the user’s attention focused on the dialogue. For longer delays, users will want to perform other tasks while waiting for the computer to finish, so they should be given feedback indicating when the computer expects to be done. Feedback during the delay is especially important if the response time is likely to be highly variable, since users will then not know what to expect.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bucketing timeframes by attention span (based on &lt;a href=&quot;https://calibreapp.com/blog/perceived-performance#how-do-people-perceive-time-and-speed&quot;&gt;Steve Souw’s definitions and research by Rina A. Doherty and Paul Sorenson&lt;/a&gt;) proves more useful when assessing performance depending on length of the operation and perceived complexity:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attention&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Response time&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Attentive&lt;/td&gt;
&lt;td&gt;Instantaneous&lt;/td&gt;
&lt;td&gt;below 300ms&lt;/td&gt;
&lt;td&gt;Perceived as closed-loop system, where people are in direct control.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Immediate&lt;/td&gt;
&lt;td&gt;300ms–1 sec&lt;/td&gt;
&lt;td&gt;Perceived as easy to perform.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Transient&lt;/td&gt;
&lt;td&gt;1–5 sec&lt;/td&gt;
&lt;td&gt;Perceived as requiring some simple processing, but people still feel like they are making continuous progress. People are unlikely to disengage from task flow.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Attention span&lt;/td&gt;
&lt;td&gt;5–10 sec&lt;/td&gt;
&lt;td&gt;Perceived as requiring more wait time. People need informative feedback to stay engaged.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-attentive&lt;/td&gt;
&lt;td&gt;Non-attentive&lt;/td&gt;
&lt;td&gt;10 sec–5 min&lt;/td&gt;
&lt;td&gt;Perceived as requiring more complex processing. People are likely to disengage and multi-task.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Walk-away&lt;/td&gt;
&lt;td&gt;above 5 min&lt;/td&gt;
&lt;td&gt;Perceived as requiring intensive processing. People won’t stay engaged with the task.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;my-story-with-perceived-performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#my-story-with-perceived-performance&quot; aria-label=&quot;my story with perceived performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;My Story with Perceived Performance&lt;/h2&gt;
&lt;p&gt;When I started my career as a frontend developer about 7-8 years ago, I thought I was doing everything right. Every time I fetched data, I showed a loading spinner. However, my boss kept complaining about how slow everything felt.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/3f8090d01022f337b8bc3df25a8a9460/perceived-performance-01.gif&quot; alt=&quot;Perceived Performance&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;I checked the network tab, and everything seemed fine. The response time was less than 300ms, which is considered instantaneous.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b15ce9613f77de53eb6cebea912035c1/029a1/perceived-performance-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.24999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHlzRMUX//EABcQAQADAAAAAAAAAAAAAAAAAAEQESD/2gAIAQEAAQUCcWx//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFhAAAwAAAAAAAAAAAAAAAAAAECAx/9oACAEBAAY/Ako//8QAGBAAAwEBAAAAAAAAAAAAAAAAARARADH/2gAIAQEAAT8h4CmJkc//2gAMAwEAAgADAAAAEAcP/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8Qp//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABsQAQEAAwADAAAAAAAAAAAAAAEAESExQWFx/9oACAEBAAE/EE7OYtQEJkR4ZG968Hy//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Perceived Performance&quot;
        title=&quot;Perceived Performance&quot;
        src=&quot;/static/b15ce9613f77de53eb6cebea912035c1/6a068/perceived-performance-02.jpg&quot;
        srcset=&quot;/static/b15ce9613f77de53eb6cebea912035c1/09b79/perceived-performance-02.jpg 240w,
/static/b15ce9613f77de53eb6cebea912035c1/7cc5e/perceived-performance-02.jpg 480w,
/static/b15ce9613f77de53eb6cebea912035c1/6a068/perceived-performance-02.jpg 960w,
/static/b15ce9613f77de53eb6cebea912035c1/029a1/perceived-performance-02.jpg 1088w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The code itself was fairly simple, just a few states and a data fetch. What could be wrong?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useFetchVehicle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vehicleId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vehicle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setVehicle&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Vehicle &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setLoading&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setError&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchVehicle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vehicleId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setVehicle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; Error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;vehicleId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; vehicle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I was confused. I thought I was doing everything right. However, my boss asked, “Why is the spinner on forever?”&lt;/p&gt;
&lt;p&gt;After much discussion, I realized that the spinner was the problem. For a resource as simple as the vehicle list, the spinner should not be there. Showing the spinner is necessary if the user’s network is slow or our API server is slow. But most of our users have high-speed internet, and our API server is fast. So the spinner was not needed. We decided to only show the spinner if the response time was more than 300ms.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6a35753073d82f2c1b42bb304ef52a69/cecd0/perceived-performance-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd1YED//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAEFAl//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAZEAADAAMAAAAAAAAAAAAAAAAAAREQUYH/2gAIAQEAAT8h4K6xEREP/9oADAMBAAIAAwAAABBDz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAICAgMAAAAAAAAAAAAAAAERACFBURDh8P/aAAgBAQABPxAghoTe+ogBJvy4diOxAAoCf//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Perceived Performance&quot;
        title=&quot;Perceived Performance&quot;
        src=&quot;/static/6a35753073d82f2c1b42bb304ef52a69/6a068/perceived-performance-03.jpg&quot;
        srcset=&quot;/static/6a35753073d82f2c1b42bb304ef52a69/09b79/perceived-performance-03.jpg 240w,
/static/6a35753073d82f2c1b42bb304ef52a69/7cc5e/perceived-performance-03.jpg 480w,
/static/6a35753073d82f2c1b42bb304ef52a69/6a068/perceived-performance-03.jpg 960w,
/static/6a35753073d82f2c1b42bb304ef52a69/644c5/perceived-performance-03.jpg 1440w,
/static/6a35753073d82f2c1b42bb304ef52a69/0f98f/perceived-performance-03.jpg 1920w,
/static/6a35753073d82f2c1b42bb304ef52a69/cecd0/perceived-performance-03.jpg 5120w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To implement this, I added a delay of 300ms before showing the spinner. If the response time was less than 300ms, the spinner would not be shown.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;export const useFetchVehicle = (vehicleId: string | undefined) =&gt; {
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; useEffect(() =&gt; {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   let timer: ReturnType&amp;lt;typeof setTimeout&gt;;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   const init = async () =&gt; {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     try {
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        timer = setTimeout(() =&gt; {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;          setLoading(true);
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        }, 300);
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       const data = await fetchVehicle(vehicleId);
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       setVehicle(data);
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     } catch (err) {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       setError((err as Error).message);
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     } finally {
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        if (timer) clearTimeout(timer);
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       setLoading(false);
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     }
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   };
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   init();
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; }, [vehicleId]);
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; return { vehicle, loading, error };
&lt;/span&gt;};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result was very satisfying.&lt;/p&gt;
&lt;p&gt;The spinner doesn’t show up anymore when the response time is less than 300ms. Users feel like the website is faster even though the actual response time is the same.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/a36da559fa4692ddb1f1894cc3e83fd9/perceived-performance-04.gif&quot; alt=&quot;Perceived Performance&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;The spinner is only shown when the response time is more than 300ms.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/c2dd06121ead0048e51f3106c9bb9104/perceived-performance-05.gif&quot; alt=&quot;Perceived Performance&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Perceived performance is just as important as actual performance. By understanding the psychology of waiting, we can design web experiences that feel faster to users.&lt;/p&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://davidmaister.com/articles/the-psychology-of-waiting-lines/&quot;&gt;The Psychology of Waiting Lines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nngroup.com/articles/response-times-3-important-limits/&quot;&gt;Response Times: The 3 Important Limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://calibreapp.com/blog/perceived-performance#how-do-people-perceive-time-and-speed&quot;&gt;How do people perceive time and speed?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[zsh history not working after VSCode upgrade]]></title><link>https://trungvose.comvscode-zsh-history/</link><guid isPermaLink="false">https://trungvose.comvscode-zsh-history/</guid><pubDate>Mon, 12 Dec 2022 07:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;I recently updated my VSCode to the latest version and found that the zsh history is not working anymore. That’s how it looks when running the &lt;code class=&quot;language-text&quot;&gt;history&lt;/code&gt; command with only a few commands showing up.&lt;/p&gt;
&lt;p&gt;When running &lt;code class=&quot;language-text&quot;&gt;echo $HISTFILE&lt;/code&gt;, it shows some random path that I don’t recognize. It should be &lt;code class=&quot;language-text&quot;&gt;~/.zsh_history&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3de38986fd73e16158a4a17be09bc276/5c489/zsh-history-not-working-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAcqoQD//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAEFAn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAQ/9oACAEBAAY/An//xAAWEAEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQEAAT8hBTbLL//aAAwDAQACAAMAAAAQC8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAgEBPxBn/8QAGRABAAMBAQAAAAAAAAAAAAAAAQARIVGh/9oACAEBAAE/EFIrAVYu9bluez//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zsh history not working&quot;
        title=&quot;zsh history not working&quot;
        src=&quot;/static/3de38986fd73e16158a4a17be09bc276/6a068/zsh-history-not-working-01.jpg&quot;
        srcset=&quot;/static/3de38986fd73e16158a4a17be09bc276/09b79/zsh-history-not-working-01.jpg 240w,
/static/3de38986fd73e16158a4a17be09bc276/7cc5e/zsh-history-not-working-01.jpg 480w,
/static/3de38986fd73e16158a4a17be09bc276/6a068/zsh-history-not-working-01.jpg 960w,
/static/3de38986fd73e16158a4a17be09bc276/644c5/zsh-history-not-working-01.jpg 1440w,
/static/3de38986fd73e16158a4a17be09bc276/5c489/zsh-history-not-working-01.jpg 1698w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Others have reported the same issue on the VSCode GitHub repo for VS Code 1.74.0.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/microsoft/vscode/issues/168396&quot;&gt;https://github.com/microsoft/vscode/issues/168396&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d80ccece6ac72a8c0e363ff9201b730f/a3e9c/zsh-history-not-working-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAgABBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeMjg4R//8QAFxABAQEBAAAAAAAAAAAAAAAAAiEAEP/aAAgBAQABBQLGpxd//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRABAQADAQAAAAAAAAAAAAAAAQAQETEh/9oACAEBAAE/IbwDAQDWBTkq9d3/2gAMAwEAAgADAAAAEFPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGRABAAMBAQAAAAAAAAAAAAAAAQARUUEx/9oACAEBAAE/ELohAAjyoZIYcj5ELSOjGbZatz//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zsh history not working&quot;
        title=&quot;zsh history not working&quot;
        src=&quot;/static/d80ccece6ac72a8c0e363ff9201b730f/6a068/zsh-history-not-working-02.jpg&quot;
        srcset=&quot;/static/d80ccece6ac72a8c0e363ff9201b730f/09b79/zsh-history-not-working-02.jpg 240w,
/static/d80ccece6ac72a8c0e363ff9201b730f/7cc5e/zsh-history-not-working-02.jpg 480w,
/static/d80ccece6ac72a8c0e363ff9201b730f/6a068/zsh-history-not-working-02.jpg 960w,
/static/d80ccece6ac72a8c0e363ff9201b730f/644c5/zsh-history-not-working-02.jpg 1440w,
/static/d80ccece6ac72a8c0e363ff9201b730f/0f98f/zsh-history-not-working-02.jpg 1920w,
/static/d80ccece6ac72a8c0e363ff9201b730f/a3e9c/zsh-history-not-working-02.jpg 2020w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;The solution is to add the following line to your &lt;code class=&quot;language-text&quot;&gt;~/.zshrc&lt;/code&gt; file to set the &lt;code class=&quot;language-text&quot;&gt;HISTFILE&lt;/code&gt; variable explicitly.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;HISTFILE&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.zsh_history&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After that, run &lt;code class=&quot;language-text&quot;&gt;source ~/.zshrc&lt;/code&gt; to reload the configuration file. Now the &lt;code class=&quot;language-text&quot;&gt;HISTFILE&lt;/code&gt; variable should be set correctly thus the zsh history is working again.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d976c5fe8f5553288fd6a0c284a7ab78/8449d/zsh-history-not-working-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHkzeYxB//EABUQAQEAAAAAAAAAAAAAAAAAABBB/9oACAEBAAEFAmH/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAUEAEAAAAAAAAAAAAAAAAAAAAg/9oACAEBAAY/Al//xAAYEAACAwAAAAAAAAAAAAAAAAAQEQEhMf/aAAgBAQABPyGa0psf/9oADAMBAAIAAwAAABDwz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABoQAQACAwEAAAAAAAAAAAAAAAEAERAhUTH/2gAIAQEAAT8QRpa+xS9XNdYhl6Vn/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;zsh history working&quot;
        title=&quot;zsh history working&quot;
        src=&quot;/static/d976c5fe8f5553288fd6a0c284a7ab78/6a068/zsh-history-not-working-03.jpg&quot;
        srcset=&quot;/static/d976c5fe8f5553288fd6a0c284a7ab78/09b79/zsh-history-not-working-03.jpg 240w,
/static/d976c5fe8f5553288fd6a0c284a7ab78/7cc5e/zsh-history-not-working-03.jpg 480w,
/static/d976c5fe8f5553288fd6a0c284a7ab78/6a068/zsh-history-not-working-03.jpg 960w,
/static/d976c5fe8f5553288fd6a0c284a7ab78/644c5/zsh-history-not-working-03.jpg 1440w,
/static/d976c5fe8f5553288fd6a0c284a7ab78/0f98f/zsh-history-not-working-03.jpg 1920w,
/static/d976c5fe8f5553288fd6a0c284a7ab78/8449d/zsh-history-not-working-03.jpg 2006w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The different between :focus and :focus-visible]]></title><link>https://trungvose.comfocus-and-focus-visible/</link><guid isPermaLink="false">https://trungvose.comfocus-and-focus-visible/</guid><pubDate>Sat, 19 Nov 2022 07:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Browsing the web using different input methods like a mouse, keyboard, or assistive technology devices requires clear indicators of the current interactive element for a good user experience and accessibility. While default browser stylesheets do a great job, there are cases where we may want more control over focus styling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/7ff6020a651c3b7bf722319cc0028ff9/2022-11-19-focus-visible-01.gif&quot; alt=&quot;The difference between :focus and :focus-visible&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;One common argument against focus indicators is that they appear even when you click on a component with a mouse or tap on it. Designers and stakeholders often don’t prefer this behavior.&lt;/p&gt;
&lt;p&gt;Currently, modern browsers only show default focus indicators when necessary, specifically when navigating with a keyboard. The focus outline doesn’t show up when an element is clicked or tapped; it only appears when tabbing to it using a keyboard.&lt;/p&gt;
&lt;h2 id=&quot;understanding-the-issue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#understanding-the-issue&quot; aria-label=&quot;understanding the issue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Understanding the Issue&lt;/h2&gt;
&lt;p&gt;In the provided code and example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;&amp;amp;:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #007dbc60&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline-offset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1.5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We applied a different &lt;code class=&quot;language-text&quot;&gt;outline&lt;/code&gt; color and a &lt;code class=&quot;language-text&quot;&gt;transform(1.5)&lt;/code&gt; to the element to make the focus state visually obvious.&lt;/p&gt;
&lt;p&gt;The problem arises when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigating with the keyboard, both the &lt;code class=&quot;language-text&quot;&gt;outline&lt;/code&gt; and the element grow bigger with &lt;code class=&quot;language-text&quot;&gt;transform&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Clicking with the mouse, the &lt;code class=&quot;language-text&quot;&gt;outline&lt;/code&gt; is invisible, but the element still grows bigger with &lt;code class=&quot;language-text&quot;&gt;transform&lt;/code&gt;. This behavior is not desired.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/4c8557733645c1237a64987f5dc40717/2022-11-19-focus-visible-02.gif&quot; alt=&quot;The difference between :focus and :focus-visible&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Somehow, when we click, it also captures the &lt;code class=&quot;language-text&quot;&gt;focus&lt;/code&gt; state.&lt;/p&gt;
&lt;p&gt;You may wonder why only the &lt;code class=&quot;language-text&quot;&gt;scale&lt;/code&gt; is applied, but not the &lt;code class=&quot;language-text&quot;&gt;outline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The reason is that the &lt;code class=&quot;language-text&quot;&gt;outline&lt;/code&gt; is a property that is only visible when the element is focused using &lt;code class=&quot;language-text&quot;&gt;focus-visible&lt;/code&gt; under the hood. So, when we click, the &lt;code class=&quot;language-text&quot;&gt;outline&lt;/code&gt; is not visible, but the &lt;code class=&quot;language-text&quot;&gt;scale&lt;/code&gt; is still applied.&lt;/p&gt;
&lt;p&gt;To solve this issue and show focus indicators only for users who need them, we can use the &lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt; selector.&lt;/p&gt;
&lt;h2 id=&quot;what-is-focus-visible&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-focus-visible&quot; aria-label=&quot;what is focus visible permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is &lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt; works just like &lt;code class=&quot;language-text&quot;&gt;:focus&lt;/code&gt;, but it only applies focus indicator styles to an element when it receives keyboard focus.&lt;/p&gt;
&lt;p&gt;Now, the focus indicator will only be visible to users navigating with a keyboard or keyboard-like assistive technology. Users who aren’t using a keyboard won’t even know it’s there!&lt;/p&gt;
&lt;p&gt;To fix the issue above, simply change &lt;code class=&quot;language-text&quot;&gt;:focus&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt; in our CSS:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; &amp;amp;:focus {
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &amp;amp;:focus-visible {
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; outline-color: #007dbc60;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; outline-offset: 0.5rem;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; transform: scale(1.5);
&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/96b3864142d26ea4dbbd04ee8977d0e8/2022-11-19-focus-visible-03.gif&quot; alt=&quot;The difference between :focus and :focus-visible&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;browser-support&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#browser-support&quot; aria-label=&quot;browser support permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Browser Support&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt; pseudo-class is supported by all major browsers except for IE11.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABYBAQEBAAAAAAAAAAAAAAAAAAEAAv/aAAwDAQACEAMQAAABzKGKAv8A/8QAFhAAAwAAAAAAAAAAAAAAAAAAEBFB/9oACAEBAAEFAoh//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFRABAQAAAAAAAAAAAAAAAAAAEDH/2gAIAQEABj8Cr//EABkQAAMAAwAAAAAAAAAAAAAAAAABURFhkf/aAAgBAQABPyFqHDCjWz//2gAMAwEAAgADAAAAEH/P/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/EB2f/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEB/9oACAECAQE/ELqv/8QAGxAAAgEFAAAAAAAAAAAAAAAAAAEhETFBcfD/2gAIAQEAAT8Qv8CioanCvtEo/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The difference between :focus and :focus-visible&quot;
        title=&quot;The difference between :focus and :focus-visible&quot;
        src=&quot;/static/563cc4e5f0f5577b0f145282d0e1b5f0/6a068/2022-11-19-focus-visible-04.jpg&quot;
        srcset=&quot;/static/563cc4e5f0f5577b0f145282d0e1b5f0/09b79/2022-11-19-focus-visible-04.jpg 240w,
/static/563cc4e5f0f5577b0f145282d0e1b5f0/7cc5e/2022-11-19-focus-visible-04.jpg 480w,
/static/563cc4e5f0f5577b0f145282d0e1b5f0/6a068/2022-11-19-focus-visible-04.jpg 960w,
/static/563cc4e5f0f5577b0f145282d0e1b5f0/644c5/2022-11-19-focus-visible-04.jpg 1440w,
/static/563cc4e5f0f5577b0f145282d0e1b5f0/0f98f/2022-11-19-focus-visible-04.jpg 1920w,
/static/563cc4e5f0f5577b0f145282d0e1b5f0/d3e61/2022-11-19-focus-visible-04.jpg 2850w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;support-notes--march-2023&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#support-notes--march-2023&quot; aria-label=&quot;support notes  march 2023 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Support notes — March 2023&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt; pseudo-class is now supported in all major browsers.&lt;/p&gt;
&lt;p&gt;But we recommend that default :focus styles are still provided, as a fallback for older versions, since focus indication is so critical.&lt;/p&gt;
&lt;p&gt;Read more for &lt;a href=&quot;https://www.tpgi.com/focus-visible-and-backwards-compatibility/&quot;&gt;more details and an alternative &lt;code class=&quot;language-text&quot;&gt;:focus:not(:focus-visible)&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-takeaways&quot; aria-label=&quot;key takeaways permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key Takeaways&lt;/h2&gt;
&lt;p&gt;If you want to display focus indicators only for keyboard users, utilize the &lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt; pseudo-class instead of &lt;code class=&quot;language-text&quot;&gt;:focus&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;iframe height=&quot;300&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;focus vs focus-visible&quot; src=&quot;https://codepen.io/trungvose/embed/BaVmzoL?default-tab=html%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&quot;https://codepen.io/trungvose/pen/BaVmzoL&quot;&gt;
  focus vs focus-visible&lt;/a&gt; by Trung Vo (&lt;a href=&quot;https://codepen.io/trungvose&quot;&gt;@trungk18&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[Angular 13 upgrade - Error: Unknown keyword formatMinimum]]></title><link>https://trungvose.comangular-13-error-unknown-keyword-formatMinimum/</link><guid isPermaLink="false">https://trungvose.comangular-13-error-unknown-keyword-formatMinimum/</guid><pubDate>Sun, 13 Nov 2022 07:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;I recently upgraded my Angular project from version 12 to version 13 for our massive project at &lt;a href=&quot;https://www.ascendaloyalty.com/&quot;&gt;Ascenda&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After the upgrade, I ran into an error when I tried to build the project.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;Error&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Module build &lt;span class=&quot;token function&quot;&gt;failed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;from &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;mini&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;css&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;extract&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;plugin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dist&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;loader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;

Error&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Unknown keyword formatMinimum
    at &lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ajv&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;keywords&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dist&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at &lt;span class=&quot;token function&quot;&gt;ajvKeywords&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ajv&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;keywords&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dist&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;anonymous&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;webpack&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;schema&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;utils&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dist&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;validatejs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_compile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;internal&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cjs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1101&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_extensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;js&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;internal&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cjs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1153&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;internal&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cjs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;981&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_load&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;internal&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cjs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;822&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at Module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;internal&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cjs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1005&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;internal&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cjs&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;helpers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;102&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    at Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;anonymous&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;build&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;webpack&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;node_modules&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;schema&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;utils&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;dist&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;indexjs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As we can see, the error is related to the &lt;code class=&quot;language-text&quot;&gt;formatMinimum&lt;/code&gt; keyword in the &lt;code class=&quot;language-text&quot;&gt;ajv-keywords&lt;/code&gt; package.&lt;/p&gt;
&lt;p&gt;The interesting problem is that the error only occurs on CI/CD when you delete the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; folder and reinstall the dependencies.&lt;/p&gt;
&lt;h2 id=&quot;investigation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#investigation&quot; aria-label=&quot;investigation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Investigation&lt;/h2&gt;
&lt;p&gt;First, to understand what is going on, we need to know where &lt;code class=&quot;language-text&quot;&gt;ajv-keywords&lt;/code&gt; is used in the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ls&lt;/span&gt; ajv-keywords&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/80569/2022-11-13-angular-update-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcqKiAr/xAAXEAEAAwAAAAAAAAAAAAAAAAAQESFC/9oACAEBAAEFApoyf//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAAAAgMf/aAAgBAQAGPwIq/wD/xAAZEAEAAgMAAAAAAAAAAAAAAAABEBEAITH/2gAIAQEAAT8hUaNZaweJ/9oADAMBAAIAAwAAABD7z//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABkQAQACAwAAAAAAAAAAAAAAAAEAcREgMf/aAAgBAQABPxAjlox7E267/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 13 upgrade - Error: Unknown keyword formatMinimum&quot;
        title=&quot;Angular 13 upgrade - Error: Unknown keyword formatMinimum&quot;
        src=&quot;/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/6a068/2022-11-13-angular-update-01.jpg&quot;
        srcset=&quot;/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/09b79/2022-11-13-angular-update-01.jpg 240w,
/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/7cc5e/2022-11-13-angular-update-01.jpg 480w,
/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/6a068/2022-11-13-angular-update-01.jpg 960w,
/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/644c5/2022-11-13-angular-update-01.jpg 1440w,
/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/0f98f/2022-11-13-angular-update-01.jpg 1920w,
/static/397ae5ffa6dab7b7c1d0bd3aad25f9d7/80569/2022-11-13-angular-update-01.jpg 2510w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As we can see, there are two versions of &lt;code class=&quot;language-text&quot;&gt;ajv-keywords&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;adj-keywords@3.5.2&lt;/code&gt; is used by &lt;code class=&quot;language-text&quot;&gt;schema-utils&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;adj-keywords@5.1.0&lt;/code&gt; is used by &lt;code class=&quot;language-text&quot;&gt;webpack&lt;/code&gt; and other packages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can clearly see &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; indicated that our &lt;code class=&quot;language-text&quot;&gt;adj-keywords&lt;/code&gt; version is invalid in two places.&lt;/p&gt;
&lt;p&gt;Where by &lt;code class=&quot;language-text&quot;&gt;adj-keywords@5.1.0&lt;/code&gt; is required, however &lt;code class=&quot;language-text&quot;&gt;adj-keywords@3.5.2&lt;/code&gt; is installed.&lt;/p&gt;
&lt;h2 id=&quot;npm-deduped&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#npm-deduped&quot; aria-label=&quot;npm deduped permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;npm deduped&lt;/h2&gt;
&lt;p&gt;On the above screenshot, we can see that &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; has indicated that &lt;code class=&quot;language-text&quot;&gt;adj-keywords&lt;/code&gt; was deduped.&lt;/p&gt;
&lt;p&gt;But what is &lt;code class=&quot;language-text&quot;&gt;npm deduped&lt;/code&gt;?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;deduped&lt;/code&gt; is short for “deduplicated” (duplicates were removed).&lt;/p&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://docs.npmjs.com/cli/v7/commands/npm-dedupe&quot;&gt;npm documentation&lt;/a&gt;, &lt;code class=&quot;language-text&quot;&gt;npm dedupe&lt;/code&gt; is used to remove duplicate packages.&lt;/p&gt;
&lt;p&gt;Searches the local package tree and attempts to simplify the overall structure by moving dependencies further up the tree, where they can be more effectively shared by multiple dependent packages.&lt;/p&gt;
&lt;p&gt;In other words, it looks if multiple packages have the same dependencies (meaning the same packages &lt;strong&gt;and&lt;/strong&gt; version range) and “points” them to the same package.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Meaning on our example above, despite many packages requiring &lt;code class=&quot;language-text&quot;&gt;adj-keywords@5.1.0&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; will only install this package once. Instead of installing it multiple times.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;To solve this problem, we need to execute &lt;code class=&quot;language-text&quot;&gt;npm update&lt;/code&gt; to update the npm packages.&lt;/p&gt;
&lt;p&gt;It will result in the &lt;code class=&quot;language-text&quot;&gt;package-lock.json&lt;/code&gt; file being updated and you will need to commit the changes.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; update&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After the update, we can see that the &lt;code class=&quot;language-text&quot;&gt;ajv-keywords&lt;/code&gt; version is correct without the &lt;code class=&quot;language-text&quot;&gt;invalid&lt;/code&gt; warning anymore.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bfea4ec436ee6e6bfb9b129c71f1654b/05cb6/2022-11-13-angular-update-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHKgior/8QAGBAAAgMAAAAAAAAAAAAAAAAAARAREiH/2gAIAQEAAQUCnLFf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEQMf/aAAgBAQAGPwI1z//EABoQAAICAwAAAAAAAAAAAAAAAAERABAhUZH/2gAIAQEAAT8hYYlTb0r/2gAMAwEAAgADAAAAEIgP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAEDH/2gAIAQIBAT8Qp//EABsQAAICAwEAAAAAAAAAAAAAAAABEXEhMUGR/9oACAEBAAE/EFlJuw4JNpXG79P/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 13 upgrade - Error: Unknown keyword formatMinimum&quot;
        title=&quot;Angular 13 upgrade - Error: Unknown keyword formatMinimum&quot;
        src=&quot;/static/bfea4ec436ee6e6bfb9b129c71f1654b/6a068/2022-11-13-angular-update-02.jpg&quot;
        srcset=&quot;/static/bfea4ec436ee6e6bfb9b129c71f1654b/09b79/2022-11-13-angular-update-02.jpg 240w,
/static/bfea4ec436ee6e6bfb9b129c71f1654b/7cc5e/2022-11-13-angular-update-02.jpg 480w,
/static/bfea4ec436ee6e6bfb9b129c71f1654b/6a068/2022-11-13-angular-update-02.jpg 960w,
/static/bfea4ec436ee6e6bfb9b129c71f1654b/644c5/2022-11-13-angular-update-02.jpg 1440w,
/static/bfea4ec436ee6e6bfb9b129c71f1654b/0f98f/2022-11-13-angular-update-02.jpg 1920w,
/static/bfea4ec436ee6e6bfb9b129c71f1654b/05cb6/2022-11-13-angular-update-02.jpg 2072w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;npm update&lt;/code&gt; is a helpful command to update the npm packages that you might need to use when you encounter the &lt;code class=&quot;language-text&quot;&gt;npm deduped&lt;/code&gt; problem.&lt;/p&gt;
&lt;p&gt;After resolving this problem, Our team at Ascenda also decided two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Upgrade the Angular project to version 14 straight away. Our first attempt at Angular 14 upgrade also failed due to the same &lt;code class=&quot;language-text&quot;&gt;npm deduped&lt;/code&gt; issue.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://yarnpkg.com/&quot;&gt;Yarn&lt;/a&gt; instead of &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; to manage the dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/angular/angular/blob/954f700ab44b705e7ad95b7d102f6394553d4e12/package.json#L13&quot;&gt;Angular team recommends using Yarn&lt;/a&gt; to manage the dependencies as well.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d3d81a5f67c89a9bccf3f36a135cedac/24813/2022-11-13-angular-update-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAcaCQH//xAAVEAEBAAAAAAAAAAAAAAAAAAAQQf/aAAgBAQABBQKH/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFhAAAwAAAAAAAAAAAAAAAAAAAAEQ/9oACAEBAAE/IQ5//9oADAMBAAIAAwAAABAMP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EKr/xAAbEAACAgMBAAAAAAAAAAAAAAAAARFxITFRgf/aAAgBAQABPxBQ0ndiceLPT//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular 13 upgrade - Error: Unknown keyword formatMinimum&quot;
        title=&quot;Angular 13 upgrade - Error: Unknown keyword formatMinimum&quot;
        src=&quot;/static/d3d81a5f67c89a9bccf3f36a135cedac/6a068/2022-11-13-angular-update-03.jpg&quot;
        srcset=&quot;/static/d3d81a5f67c89a9bccf3f36a135cedac/09b79/2022-11-13-angular-update-03.jpg 240w,
/static/d3d81a5f67c89a9bccf3f36a135cedac/7cc5e/2022-11-13-angular-update-03.jpg 480w,
/static/d3d81a5f67c89a9bccf3f36a135cedac/6a068/2022-11-13-angular-update-03.jpg 960w,
/static/d3d81a5f67c89a9bccf3f36a135cedac/644c5/2022-11-13-angular-update-03.jpg 1440w,
/static/d3d81a5f67c89a9bccf3f36a135cedac/0f98f/2022-11-13-angular-update-03.jpg 1920w,
/static/d3d81a5f67c89a9bccf3f36a135cedac/24813/2022-11-13-angular-update-03.jpg 2210w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why Web Performance Matters: An Introduction]]></title><description><![CDATA[Understand the importance of web performance and its impact on user experience and business success.]]></description><link>https://trungvose.comwhy-web-performance-matters-an-introduction/</link><guid isPermaLink="false">https://trungvose.comwhy-web-performance-matters-an-introduction/</guid><pubDate>Sun, 04 Sep 2022 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the fast-paced world of the web, performance is everything. Whether you’re running an e-commerce store, a personal blog, or a large-scale web application, how quickly and smoothly your site loads can make or break your user’s experience. Websites that load quickly and respond to user input in a timely fashion engage and retain users better than websites that are slow to load and feel sluggish.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5dda90817f2602872551f4554e5ecd38/25e07/why-web-performance-matters-an-introduction-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.58333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeRUjAf/xAAXEAEAAwAAAAAAAAAAAAAAAAABABEg/9oACAEBAAEFAiUZ/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhAAAgMBAQAAAAAAAAAAAAAAASEAEBFhsf/aAAgBAQABPyENLmgXlBOdr//aAAwDAQACAAMAAAAQoM//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgIDAAAAAAAAAAAAAAABACERURBBof/aAAgBAQABPxAL3ixQpWlMGo3IdRK0eP/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Why Web Performance Matters: An Introduction&quot;
        title=&quot;Why Web Performance Matters: An Introduction&quot;
        src=&quot;/static/5dda90817f2602872551f4554e5ecd38/6a068/why-web-performance-matters-an-introduction-01.jpg&quot;
        srcset=&quot;/static/5dda90817f2602872551f4554e5ecd38/09b79/why-web-performance-matters-an-introduction-01.jpg 240w,
/static/5dda90817f2602872551f4554e5ecd38/7cc5e/why-web-performance-matters-an-introduction-01.jpg 480w,
/static/5dda90817f2602872551f4554e5ecd38/6a068/why-web-performance-matters-an-introduction-01.jpg 960w,
/static/5dda90817f2602872551f4554e5ecd38/644c5/why-web-performance-matters-an-introduction-01.jpg 1440w,
/static/5dda90817f2602872551f4554e5ecd38/0f98f/why-web-performance-matters-an-introduction-01.jpg 1920w,
/static/5dda90817f2602872551f4554e5ecd38/25e07/why-web-performance-matters-an-introduction-01.jpg 3358w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;1-what-is-web-performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-what-is-web-performance&quot; aria-label=&quot;1 what is web performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. What is Web Performance?&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Performance/What_is_web_performance&quot;&gt;MDN&lt;/a&gt;, web performance is the objective measurement and perceived user experience of a website or application. This includes the following major areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reducing overall load time:&lt;/strong&gt; How long does it take for the files required to render the website to download onto the user’s computer? This is affected by latency, file size, the number of files, and other factors. Strategies include minimizing file sizes, reducing the number of HTTP requests, and using techniques like preloading to make files available sooner.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Making the site usable as soon as possible:&lt;/strong&gt; Load website assets in a sensible order so that the user can start using it quickly. Other assets can load in the background, and some can be loaded only when needed (lazy loading). The time it takes for the site to become usable after it starts loading is called time to interactive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smoothness and interactivity:&lt;/strong&gt; Does the application feel reliable and pleasurable to use? Is scrolling smooth? Are buttons clickable? Do pop-ups open quickly and animate smoothly? Best practices include using CSS animations instead of JavaScript and minimizing UI repaints due to DOM changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Perceived performance:&lt;/strong&gt; How fast a website seems to the user impacts user experience more than actual speed. Keeping users engaged while they wait, such as showing a loading spinner or useful hints, can improve perceived performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To summarize, many factors impact performance, including latency, application size, the number of DOM nodes, resource requests, JavaScript performance, and CPU load. Minimizing loading and response times and adding features to conceal latency can make the experience as available and interactive as possible, as soon as possible.&lt;/p&gt;
&lt;h2 id=&quot;2-the-impact-of-web-performance-on-user-experience&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-the-impact-of-web-performance-on-user-experience&quot; aria-label=&quot;2 the impact of web performance on user experience permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. The Impact of Web Performance on User Experience&lt;/h2&gt;
&lt;p&gt;User experience (UX) is at the heart of any successful website, and performance is a key component. Here’s why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;First Impressions Matter:&lt;/strong&gt; Users form an impression of your site within the first few seconds of loading. A slow site can cause users to leave before it finishes loading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Engagement:&lt;/strong&gt; Fast, responsive websites encourage users to stay longer, explore more content, and engage with features like forms, videos, and links. Slow sites drive users away, reducing engagement and increasing bounce rates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile Users:&lt;/strong&gt; With more people accessing the web on mobile devices, performance is even more critical. Mobile users often have slower connections, so a site that loads quickly on desktop might still be slow on mobile.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-the-business-case-for-web-performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-the-business-case-for-web-performance&quot; aria-label=&quot;3 the business case for web performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. The Business Case for Web Performance&lt;/h2&gt;
&lt;p&gt;Beyond user experience, web performance significantly impacts business metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Conversion Rates:&lt;/strong&gt; There is a direct correlation between page load time and conversion rates. A delay of even one second can lead to a significant drop in conversions, translating to lost revenue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SEO and Search Rankings:&lt;/strong&gt; Google considers page speed a ranking factor, particularly on mobile. A faster website is more likely to rank higher in search results, leading to increased visibility and traffic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customer Retention:&lt;/strong&gt; Fast-loading sites build trust and credibility, encouraging users to return. Poor performance can lead to negative perceptions and reduced customer loyalty.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;31-what-is-a-webpages-conversion-rate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31-what-is-a-webpages-conversion-rate&quot; aria-label=&quot;31 what is a webpages conversion rate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1 &lt;a href=&quot;https://www.cloudflare.com/learning/performance/more/website-performance-conversion-rates/&quot;&gt;What is a webpage’s conversion rate?&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In web terms, a user converts when they take the desired action on a webpage, such as purchasing a product, filling out a form, or clicking through to another page. The conversion rate is the percentage of visitors who convert. For example, if 100 users visit a page and 2 users click the ‘buy’ button, the conversion rate is 2%. Conversion rate optimization aims to increase this figure.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f02ad8fa343938d30866f08965e6a121/6e99d/why-web-performance-matters-an-introduction-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMBAgT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB3tbakkkf/8QAGhAAAgIDAAAAAAAAAAAAAAAAAREAAiEiMf/aAAgBAQABBQIlGuwOIEmJbv8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRAAAgMBAAAAAAAAAAAAAAAAADEBEBEh/9oACAEBAAY/AlMiynXD/8QAGxABAQEBAQADAAAAAAAAAAAAAREAIUExcYH/2gAIAQEAAT8hGYv55popDImFcXTrl/MV6nfbpfb63//aAAwDAQACAAMAAAAQY8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAWEQEBAQAAAAAAAAAAAAAAAAARARD/2gAIAQIBAT8QoZ//xAAcEAEBAQADAAMAAAAAAAAAAAABEQAhMVGBobH/2gAIAQEAAT8QclIIQ9l6lxWzCBSfGrkDz5+ZAgpyWvvGVVVQyUoEPW//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Why Web Performance Matters: An Introduction&quot;
        title=&quot;Why Web Performance Matters: An Introduction&quot;
        src=&quot;/static/f02ad8fa343938d30866f08965e6a121/6a068/why-web-performance-matters-an-introduction-02.jpg&quot;
        srcset=&quot;/static/f02ad8fa343938d30866f08965e6a121/09b79/why-web-performance-matters-an-introduction-02.jpg 240w,
/static/f02ad8fa343938d30866f08965e6a121/7cc5e/why-web-performance-matters-an-introduction-02.jpg 480w,
/static/f02ad8fa343938d30866f08965e6a121/6a068/why-web-performance-matters-an-introduction-02.jpg 960w,
/static/f02ad8fa343938d30866f08965e6a121/644c5/why-web-performance-matters-an-introduction-02.jpg 1440w,
/static/f02ad8fa343938d30866f08965e6a121/6e99d/why-web-performance-matters-an-introduction-02.jpg 1674w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conversion rate vs. page traffic&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Conversion rate is separate from total traffic. If conversion rate declines, the number of conversions goes down even if traffic remains the same. Conversely, if traffic stays the same but conversion rate increases, the number of conversions goes up.&lt;/p&gt;
&lt;h3 id=&quot;32-how-does-site-speed-affect-conversion-rates&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#32-how-does-site-speed-affect-conversion-rates&quot; aria-label=&quot;32 how does site speed affect conversion rates permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.2 How does site speed affect conversion rates?&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5d1cc9d1c46e900ed336dc2d0935433e/6912c/why-web-performance-matters-an-introduction-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB2qCwf//EABYQAAMAAAAAAAAAAAAAAAAAAAAQEf/aAAgBAQABBQIq/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAEP/aAAgBAQAGPwJ//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEQQf/aAAgBAQABPyHRR//aAAwDAQACAAMAAAAQc8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAgIDAAAAAAAAAAAAAAABABEhMWGR4f/aAAgBAQABPxDImnryMbGubxBEsZ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Why Web Performance Matters: An Introduction&quot;
        title=&quot;Why Web Performance Matters: An Introduction&quot;
        src=&quot;/static/5d1cc9d1c46e900ed336dc2d0935433e/6a068/why-web-performance-matters-an-introduction-03.jpg&quot;
        srcset=&quot;/static/5d1cc9d1c46e900ed336dc2d0935433e/09b79/why-web-performance-matters-an-introduction-03.jpg 240w,
/static/5d1cc9d1c46e900ed336dc2d0935433e/7cc5e/why-web-performance-matters-an-introduction-03.jpg 480w,
/static/5d1cc9d1c46e900ed336dc2d0935433e/6a068/why-web-performance-matters-an-introduction-03.jpg 960w,
/static/5d1cc9d1c46e900ed336dc2d0935433e/644c5/why-web-performance-matters-an-introduction-03.jpg 1440w,
/static/5d1cc9d1c46e900ed336dc2d0935433e/6912c/why-web-performance-matters-an-introduction-03.jpg 1692w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Website performance has a measurable effect on conversion rates. Studies show that faster page speed results in better conversion rates. For example, 47% of customers expect a webpage to load in 2 seconds or less, according to &lt;a href=&quot;https://blog.hubspot.com/marketing/page-load-time-conversion-rates&quot;&gt;skilled.co&lt;/a&gt;. Testing by mPulse Mobile found that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pages loading in 2.4 seconds had a 1.9% conversion rate&lt;/li&gt;
&lt;li&gt;At 3.3 seconds, conversion rate was 1.5%&lt;/li&gt;
&lt;li&gt;At 4.2 seconds, conversion rate was less than 1%&lt;/li&gt;
&lt;li&gt;At 5.7+ seconds, conversion rate was 0.6%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other companies have experienced similar results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Walmart found that for every 1 second improvement in page load time, conversions increased by 2%&lt;/li&gt;
&lt;li&gt;COOK increased conversions by 7% by reducing page load time by 0.85 seconds&lt;/li&gt;
&lt;li&gt;Mobify found that each 100ms improvement in homepage load time resulted in a 1.11% increase in conversion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These small increases in conversion significantly impact revenue. For example, if an e-commerce site generates 10 million dollars in sales per year, a 2% increase in conversion rate from a 1-second improvement in load time (as in the Walmart case study) results in a $200,000 increase in revenue.&lt;/p&gt;
&lt;h3 id=&quot;33-what-other-factors-besides-page-performance-affect-conversion-rate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#33-what-other-factors-besides-page-performance-affect-conversion-rate&quot; aria-label=&quot;33 what other factors besides page performance affect conversion rate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.3 What other factors besides page performance affect conversion rate?&lt;/h3&gt;
&lt;p&gt;Conversion rate is also affected by page design, layout, text, images, and more. If it’s unclear what action a visitor should take or if too many options are presented, they may leave the page. Regardless, website speed optimization should improve conversion rates even if other areas need optimization.&lt;/p&gt;
&lt;h3 id=&quot;34-website-speed-seo-and-conversion-rate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#34-website-speed-seo-and-conversion-rate&quot; aria-label=&quot;34 website speed seo and conversion rate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.4 Website speed, SEO, and conversion rate&lt;/h3&gt;
&lt;p&gt;Page speed affects both conversion rate and &lt;a href=&quot;https://www.cloudflare.com/learning/performance/how-website-speed-boosts-seo/&quot;&gt;SEO&lt;/a&gt;. Improving it can increase both total traffic and conversion rate.&lt;/p&gt;
&lt;p&gt;See the following screenshot from my blog (updated 2024) from Google Search Console highlighting the good performance of the website:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/284d3867bef39ffbdc2bff881f02f622/4e564/why-web-performance-matters-an-introduction-04.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAGAABAAMBAAAAAAAAAAAAAAAAAAEDBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAAB69esTIa//8QAFxABAQEBAAAAAAAAAAAAAAAAAgEDIP/aAAgBAQABBQJak3JGc//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAEDAQE/AYf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAACAwEAAAAAAAAAAAAAAAABEQAgMlH/2gAIAQEABj8CRc0S+1//xAAcEAABAwUAAAAAAAAAAAAAAAABABARITFBYYH/2gAIAQEAAT8hLawaRKJp2waFxv/aAAwDAQACAAMAAAAQPw//xAAVEQEBAAAAAAAAAAAAAAAAAAAREP/aAAgBAwEBPxBKz//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxCFf//EABoQAAMAAwEAAAAAAAAAAAAAAAABESFRkTH/2gAIAQEAAT8QSBTHBjTRJDoi0iPYuEWnCn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Why Web Performance Matters: An Introduction&quot;
        title=&quot;Why Web Performance Matters: An Introduction&quot;
        src=&quot;/static/284d3867bef39ffbdc2bff881f02f622/6a068/why-web-performance-matters-an-introduction-04.jpg&quot;
        srcset=&quot;/static/284d3867bef39ffbdc2bff881f02f622/09b79/why-web-performance-matters-an-introduction-04.jpg 240w,
/static/284d3867bef39ffbdc2bff881f02f622/7cc5e/why-web-performance-matters-an-introduction-04.jpg 480w,
/static/284d3867bef39ffbdc2bff881f02f622/6a068/why-web-performance-matters-an-introduction-04.jpg 960w,
/static/284d3867bef39ffbdc2bff881f02f622/644c5/why-web-performance-matters-an-introduction-04.jpg 1440w,
/static/284d3867bef39ffbdc2bff881f02f622/4e564/why-web-performance-matters-an-introduction-04.jpg 1900w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Web performance is crucial for user experience, search rankings, and business success. By understanding its importance and optimizing your site, you can create a faster, more engaging, and more successful web presence. In upcoming posts, we’ll dive deeper into specific techniques and best practices for enhancing web performance, starting with the all-important Core Web Vitals.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ngIf - Store the conditional result in a variable]]></title><link>https://trungvose.comngif-as/</link><guid isPermaLink="false">https://trungvose.comngif-as/</guid><pubDate>Wed, 31 Aug 2022 13:45:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/88633d2d063a0b5269d3bd36cf938035/00e09/ngif-as-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACNElEQVQ4y5WSTU/UQBjH9zMYDwjCbt87nbYz02nBXURPigjB3W5ZCCFRP4BHD0YPBvUm4SBET0qMB9hEEw+AxosuH6tJ/6Yd5EWQxMMvz69Pn/nnaaa1nf2DfHt3kPf3BvnO/iDv/yfqzEHe/3aQ7+z9ymtPnz3HyoMM7eUlpL0e0sXFU3R6vQspZ+a7KR49foLt3Z+oddK0MLTLhW5dLer1oWKsPqTqmKLRuHIhmj5SDA9fKm7NTBcfv3wvau1sAYl0cX1KotXimJzkuNYM0WoJTE0JSElAPPMMbgkxQKgFwxzFnblZvO/vonavm8F3DQShC+rb8H1b1cCBH9iwHQ2m1TiDZWvVuzJU00cwPXtXBba7GVioI05otU2ceJDSRSQJkoQgjj0k4xQTExRxTKq50oVw4Lh6FaifDJzvdsF8C0nCICKKSFJw4UHGAaKIVr3SReRV/ThRTn3rHxtmGYhTB6EGHLccUCjXj5x4+uHn12HZjcrP3zDtIops3LiZoNlk4MIGYxaC0IIQNji3ETILUjrwPKMKOonjGmhoI7j9J7CTLSAk5aWoA+QEHj3tChN+YIJSs+r7gQXHGTu+5bl2ByHT0Gyx6peRsYswNEB9HZyblfuB8iDQj/qle1QDYyY8bxTTszMqcPn+Q8SCIx6XiGIBLhlYFIJHDEyEiui4HvUPnUsOGhCki0vY+ryP2sZWHy/X32L19cYhm3ixtonVNVX/ZvWc51fr7/DmwzY+ff2B33go3jebGL22AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ngIf - Storing a conditional result in a variable&quot;
        title=&quot;ngIf - Storing a conditional result in a variable&quot;
        src=&quot;/static/88633d2d063a0b5269d3bd36cf938035/d9199/ngif-as-01.png&quot;
        srcset=&quot;/static/88633d2d063a0b5269d3bd36cf938035/8ff5a/ngif-as-01.png 240w,
/static/88633d2d063a0b5269d3bd36cf938035/e85cb/ngif-as-01.png 480w,
/static/88633d2d063a0b5269d3bd36cf938035/d9199/ngif-as-01.png 960w,
/static/88633d2d063a0b5269d3bd36cf938035/07a9c/ngif-as-01.png 1440w,
/static/88633d2d063a0b5269d3bd36cf938035/00e09/ngif-as-01.png 1854w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Sometimes we need to do an &lt;code class=&quot;language-text&quot;&gt;*ngIf&lt;/code&gt; with a complex condition, and we render the same result into the UI. For instance:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;somethingThatCanBeVeryLong&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ somethingThatCanBeVeryLong }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;settings.data.details.groupName | someHelperPipe:&apos;accountNumber&apos;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ settings.data.details.groupName | someHelperPipe:&apos;accountNumber&apos; }}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this case, we can use the &lt;code class=&quot;language-text&quot;&gt;as&lt;/code&gt; keyword to store the condition as a local variable and use it in the template. We are probably familiar with this syntax in async pipe &lt;code class=&quot;language-text&quot;&gt;*ngIf=&quot;users$ | async as users&quot;&lt;/code&gt;, but we can use it in any other case with ngIf.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;settings.data.details.groupName | someHelperPipe:&apos;accountNumber&apos; as accountNumber&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ accountNumber }}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;1️⃣-when-is-this-useful&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1%EF%B8%8F%E2%83%A3-when-is-this-useful&quot; aria-label=&quot;1️⃣ when is this useful permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1️⃣ When is this useful?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Access deep properties in an object&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span
  &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ngIf&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;settings.data?.details?.groupName as groupName&quot;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; groupName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The condition includes pipe, and you don’t want to apply the pipe again&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span
  &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ngIf&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;settings.data.details.groupName | someHelperPipe:&apos;accountNumber&apos; as accountNumber&quot;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; accountNumber &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The condition includes a function call, and you don’t want to call it again. Obviously you don’t want to use a function call in the template, but sometimes you are lazy and just use it anyway 😂&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read this article to find out &lt;a href=&quot;https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496&quot;&gt;Why you should never use function calls in Angular template expressions&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;generateAccountNumber(settings.data.details.groupName) as accountNumber&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ accountNumber }}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;2️⃣-how-does-it-work-under-the-hood&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2%EF%B8%8F%E2%83%A3-how-does-it-work-under-the-hood&quot; aria-label=&quot;2️⃣ how does it work under the hood permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2️⃣ How does it work under the hood?&lt;/h2&gt;
&lt;p&gt;Yeah, it’s magic that happens during template compilation.&lt;/p&gt;
&lt;p&gt;The template below&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;user$ | async as user&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;is just sugar for:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[ngIf]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;user$ | async&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;let-user&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ngIf&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On Angular source code, it sets the context to the same condition input -&gt; &lt;a href=&quot;https://github.com/angular/angular/blob/main/packages/common/src/directives/ng_if.ts#L171&quot;&gt;common/src/directives/ng_if.ts#L171&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ngIf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;condition&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$implicit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ngIf &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; condition&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_updateView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;3️⃣-how-as-works&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3%EF%B8%8F%E2%83%A3-how-as-works&quot; aria-label=&quot;3️⃣ how as works permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3️⃣ How &lt;code class=&quot;language-text&quot;&gt;as&lt;/code&gt; works?&lt;/h2&gt;
&lt;p&gt;In short, the compiler sees &lt;code class=&quot;language-text&quot;&gt;as&lt;/code&gt; keyword and do some magic so that it is equivalent to the &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; syntax.&lt;/p&gt;
&lt;p&gt;See this &lt;a href=&quot;https://stackoverflow.com/a/46616473/3375906&quot;&gt;yurzui’s stackoverflow.com answer&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;Read more on &lt;a href=&quot;https://angular.io/api/common/NgIf#storing-a-conditional-result-in-a-variable&quot;&gt;*ngIf - Storing a conditional result in a variable.&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Common use cases and solutions for accessibility in Angular]]></title><link>https://trungvose.comangular-accessibility-a11y/</link><guid isPermaLink="false">https://trungvose.comangular-accessibility-a11y/</guid><pubDate>Sun, 31 Jul 2022 03:45:00 GMT</pubDate><content:encoded>&lt;p&gt;At &lt;a href=&quot;https://www.ascendaloyalty.com/&quot;&gt;Ascenda&lt;/a&gt;, we built a suite of products that bring effortless rewards technology for financial services. One critical requirement is making the application accessible to all users. This blog will document the everyday use cases and solutions for accessibility in Angular that I have encountered along the way.&lt;/p&gt;
&lt;h2 id=&quot;what-is-accessibility&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-accessibility&quot; aria-label=&quot;what is accessibility permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is accessibility?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Broadly speaking, when we say a site is accessible, we mean that the site’s content is available, and its functionality can be operated, by literally &lt;code class=&quot;language-text&quot;&gt;anyone&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From &lt;a href=&quot;https://web.dev/accessibility/#what_is_accessibility&quot;&gt;web.dev/accessibility&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While working with accessibility or a11y in short, you will hear a lot about &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/quickref/&quot;&gt;Web Content Accessibility Guidelines (WCAG)&lt;/a&gt;, a set of guidelines that define how to make web content accessible. However, WCAG can also be a bit overwhelming. To help mitigate this, the &lt;a href=&quot;https://webaim.org/&quot;&gt;WebAIM (Web Accessibility in Mind)&lt;/a&gt; group has distilled the WCAG guidelines into an easy-to-follow checklist, targeted specifically for web content.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://webaim.org/standards/wcag/checklist&quot;&gt;WebAIM checklist&lt;/a&gt; can give you a short, high-level summary of what you need to implement, while also linking to the underlying WCAG specification if you need an expanded definition.&lt;/p&gt;
&lt;h2 id=&quot;compliances-levels&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compliances-levels&quot; aria-label=&quot;compliances levels permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compliances Levels&lt;/h2&gt;
&lt;p&gt;At &lt;a href=&quot;https://www.ascendaloyalty.com/&quot;&gt;Ascenda&lt;/a&gt;’s core fintech platform, many stakeholders are involved in making the application accessible, including product people, developers, designers, and QA engineers. For some of you that don’t know, the WCAG guidelines are classified into three conformance levels. We followed &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/quickref/&quot;&gt;WCAG 2.1&lt;/a&gt; Level A and AA criteria.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level A - Considered the least strict, Level A success criteria are essential for every website. If your website doesn’t conform with WCAG Level A, it may have serious accessibility issues that prevent users with disabilities from using it.&lt;/li&gt;
&lt;li&gt;Level AA - Websites that conform with WCAG Level AA can be considered reasonably accessible for most users. &lt;code class=&quot;language-text&quot;&gt;Most websites&lt;/code&gt; should aim for Level AA conformance. To meet this goal, content must conform with all Level AA and Level A success criteria.&lt;/li&gt;
&lt;li&gt;Level AAA - Digital content that conforms with WCAG Level AAA is considered optimally accessible. To earn Level AAA conformance, content must pass every guideline in WCAG (including Level AA and Level A success criteria). However, some Level AAA success criteria are extremely strict, and some types of content cannot conform with every guideline at this level.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our WCAG Ascenda Guideline has been rewritten to help understand WHAT is each criterion and HOW we could meet its requirements easier for all of the stakeholders. Following is an example of how it has been simplify for our team, hope you get the idea. I omit the section where we map each criterion to our product.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Criterion&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Level&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Type&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;1.1.1 Non-text Content&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Tech &amp;#x26; Design&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Provide alt-text for non-text content (Except for 1. decorative image 2. visual formatting 3. invisible elements)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;1.3.1 Info and Relationships&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Tech&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Header, title and subtitle have various heading levels according to the content structure; For all pages, there is one and only one heading level 1 item.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;1.4.1 Use of Color&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Design&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Don’t use presentation that relies solely on colour. This requirement is color specific while the 1.3.3. covers all other visual elements.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;1.3.5 Identify Input Purpose&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;AA&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Tech&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Form fields can be recognized and auto-filled by browser.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And lot of more.&lt;/p&gt;
&lt;h2 id=&quot;testing-tools&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#testing-tools&quot; aria-label=&quot;testing tools permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Testing Tools&lt;/h2&gt;
&lt;p&gt;We start with the following tools for testing&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keyboard&lt;/li&gt;
&lt;li&gt;Screen reader&lt;/li&gt;
&lt;li&gt;Page Magnifier&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;keyboard&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keyboard&quot; aria-label=&quot;keyboard permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Keyboard&lt;/h3&gt;
&lt;p&gt;Keyboard accessibility is one of the most important aspects of web accessibility. Potential problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Focus indicator&lt;/strong&gt;: a sighted keyboard user must be provided with a visual indicator of the element that currently has keyboard focus.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Navigation order&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Items that cannot receive keyboard focus&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inaccessible custom widgets&lt;/strong&gt;: complex menu, sliders, dialog, tab panel, etc. must all be built to support keyboard a11y.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Length navigation&lt;/strong&gt;: tabbing through length navigation maybe particularly demanding for users with motor disabilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;screen-reader&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#screen-reader&quot; aria-label=&quot;screen reader permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Screen Reader&lt;/h3&gt;
&lt;p&gt;A “screen reader” is a software application that convert the text displayed on a computer screen into synthesized speech. Potential problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Elements does not have correct alt&lt;/li&gt;
&lt;li&gt;Language of the page cannot be determined&lt;/li&gt;
&lt;li&gt;Incorrect text pronunciation of foreign language&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;voiceover&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#voiceover&quot; aria-label=&quot;voiceover permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;VoiceOver&lt;/h4&gt;
&lt;p&gt;VoiceOver is a screen reader that is available for Mac and iOS. It is a free software application that can be used to read aloud text on a screen.&lt;/p&gt;
&lt;p&gt;1️⃣ How to start VoiceOver?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Command&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;F5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Command&lt;/code&gt; + press touch ID three times&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;System Preferences&lt;/code&gt; &gt; &lt;code class=&quot;language-text&quot;&gt;Accessibility&lt;/code&gt; &gt; &lt;code class=&quot;language-text&quot;&gt;VoiceOver&lt;/code&gt; and selecting &lt;code class=&quot;language-text&quot;&gt;Enable VoiceOver&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The VoiceOver Activation keys (called &lt;strong&gt;VO&lt;/strong&gt; keys) are &lt;code class=&quot;language-text&quot;&gt;control&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;option&lt;/code&gt;. These keys are used to access special VoiceOver commands and functions and will be referenced simply as &lt;code class=&quot;language-text&quot;&gt;VO&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;2️⃣ Basic useful commands&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;VO&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;: Start reading&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;control&lt;/code&gt;: Stop Reading&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;VO&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;→&lt;/code&gt;: Read next item&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;VO&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;←&lt;/code&gt;: Read previous item&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;VO&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;U&lt;/code&gt;: Navigate by various items such as Heading.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;View &lt;a href=&quot;https://webaim.org/articles/voiceover/&quot;&gt;full commands&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;3️⃣ Example&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/5663e94e252478e4d321a10b7a6a84d2/voice-over-01.gif&quot; alt=&quot;MacOS Voiceover&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;common-use-cases&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#common-use-cases&quot; aria-label=&quot;common use cases permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Common Use Cases&lt;/h2&gt;
&lt;h3 id=&quot;1-visually-break-line-but-voiceover-need-to-speak-as-one-line&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-visually-break-line-but-voiceover-need-to-speak-as-one-line&quot; aria-label=&quot;1 visually break line but voiceover need to speak as one line permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Visually break line, but Voiceover need to speak as one line&lt;/h3&gt;
&lt;h4 id=&quot;-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-problem&quot; aria-label=&quot; problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;❌ Problem&lt;/h4&gt;
&lt;p&gt;Usually, you’ll have to use &lt;code class=&quot;language-text&quot;&gt;&amp;lt;br&gt;&lt;/code&gt; tag to break line on the HTML code to make a new line. Why? The design will look nicely with two lines, why not? But if you need to do it for a heading, the Voiceover won’t be able to read it as one line. See the screenshot below for an example.&lt;/p&gt;
&lt;p&gt;If I have this HTML&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;break-line&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    &amp;lt;h2&gt;This is a great introduction &amp;lt;br /&gt; and I need to be displayed in two lines&amp;lt;/h2&gt;
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s how screen reader will read it:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7a3ab7f8d39ba15fa0f0fc7817cbc872/2d2e5/001-break-line-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIEAQP/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGp7NEOof/EABoQAAIDAQEAAAAAAAAAAAAAAAECAAMSExD/2gAIAQEAAQUC1TFNRPNJzSYUef/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABwQAAICAgMAAAAAAAAAAAAAAAECAJEQMhIhcf/aAAgBAQAGPwLZanEFCfJqtTRanSisf//EABoQAQACAwEAAAAAAAAAAAAAAAEAEBExUfH/2gAIAQEAAT8hjz6zoqTzEF5G9Kf/2gAMAwEAAgADAAAAEIAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHxABAAECBwEAAAAAAAAAAAAAAQARIRAxQWGR0fDx/9oACAEBAAE/EEbPH6m910tCfKSpdretIcM8gCYf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Visually break line, but Voiceover need to speak as one line&quot;
        title=&quot;Visually break line, but Voiceover need to speak as one line&quot;
        src=&quot;/static/7a3ab7f8d39ba15fa0f0fc7817cbc872/6a068/001-break-line-01.jpg&quot;
        srcset=&quot;/static/7a3ab7f8d39ba15fa0f0fc7817cbc872/09b79/001-break-line-01.jpg 240w,
/static/7a3ab7f8d39ba15fa0f0fc7817cbc872/7cc5e/001-break-line-01.jpg 480w,
/static/7a3ab7f8d39ba15fa0f0fc7817cbc872/6a068/001-break-line-01.jpg 960w,
/static/7a3ab7f8d39ba15fa0f0fc7817cbc872/644c5/001-break-line-01.jpg 1440w,
/static/7a3ab7f8d39ba15fa0f0fc7817cbc872/2d2e5/001-break-line-01.jpg 1780w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You see it right, suddenly there is additional &lt;code class=&quot;language-text&quot;&gt;3 items&lt;/code&gt; in the heading because we have &lt;code class=&quot;language-text&quot;&gt;&amp;lt;br&gt;&lt;/code&gt; tag that break the sentence into two parts + the &lt;code class=&quot;language-text&quot;&gt;br&lt;/code&gt; itself.&lt;/p&gt;
&lt;h4 id=&quot;-how-to-to-fix-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-how-to-to-fix-it&quot; aria-label=&quot; how to to fix it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ How to to fix it?&lt;/h4&gt;
&lt;p&gt;Instead of using &lt;code class=&quot;language-text&quot;&gt;br&lt;/code&gt;, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the new line character &lt;code class=&quot;language-text&quot;&gt;\n&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;innerHTML&lt;/code&gt; for rendering.&lt;/li&gt;
&lt;li&gt;Apply &lt;code class=&quot;language-text&quot;&gt;white-space: pre-line&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;break-line&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    &amp;lt;h2 [innerHTML]=&quot;&apos;This is a great introduction \n and I need to be displayed in two lines&apos;&quot;&gt;&amp;lt;/h2&gt;
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  styles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      h2 {
        white-space: pre-line;
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s result, no more the annoying &lt;code class=&quot;language-text&quot;&gt;3 items&lt;/code&gt; in the heading!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5346f0ba7c14b922485129c1b96bfe21/3f50b/001-break-line-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.16666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAUDBP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdueaLiAP//EABwQAAEDBQAAAAAAAAAAAAAAAAIAAxEBBBAUMf/aAAgBAQABBQInmI2UFwzUZXcf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHRAAAQIHAAAAAAAAAAAAAAAAAQBxAhARISIxMv/aAAgBAQAGPwI0N2XECzABZaE//8QAHBABAAIBBQAAAAAAAAAAAAAAAQARECExQVFh/9oACAEBAAE/IQVbTQd8Cc0sHRy+AeAl1P/aAAwDAQACAAMAAAAQaO//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCn/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qh//EAB0QAQACAgIDAAAAAAAAAAAAAAEAESFBMVFhcfH/2gAIAQEAAT8QdgfCBe6lcAfPyFrec41rNRIc1Gxiq1Q0EUk6Z//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Visually break line, but Voiceover need to speak as one line&quot;
        title=&quot;Visually break line, but Voiceover need to speak as one line&quot;
        src=&quot;/static/5346f0ba7c14b922485129c1b96bfe21/6a068/001-break-line-02.jpg&quot;
        srcset=&quot;/static/5346f0ba7c14b922485129c1b96bfe21/09b79/001-break-line-02.jpg 240w,
/static/5346f0ba7c14b922485129c1b96bfe21/7cc5e/001-break-line-02.jpg 480w,
/static/5346f0ba7c14b922485129c1b96bfe21/6a068/001-break-line-02.jpg 960w,
/static/5346f0ba7c14b922485129c1b96bfe21/644c5/001-break-line-02.jpg 1440w,
/static/5346f0ba7c14b922485129c1b96bfe21/0f98f/001-break-line-02.jpg 1920w,
/static/5346f0ba7c14b922485129c1b96bfe21/3f50b/001-break-line-02.jpg 2064w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-a11y-common-use-cases?file=src/app/1-break-line/break-line.component.ts&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resources&quot; aria-label=&quot;resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resources&lt;/h2&gt;
&lt;p&gt;The following articles/blogs/websites are resources for learning about accessibility in Angular that I went through. Hope you find them useful!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://webaim.org/articles/voiceover/&quot;&gt;https://webaim.org/articles/voiceover/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/&quot;&gt;https://www.w3.org/WAI/ARIA/apg/patterns/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/use-semantic-html/&quot;&gt;https://web.dev/use-semantic-html/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=arMgwKY52Bs&quot;&gt;Semantic markup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)]]></title><link>https://trungvose.comcannot-install-in-homebrew-on-arm-processor-in-intel-default-prefix/</link><guid isPermaLink="false">https://trungvose.comcannot-install-in-homebrew-on-arm-processor-in-intel-default-prefix/</guid><pubDate>Sat, 11 Jun 2022 03:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;I am setting my MacOS fresh again and setting up my terminal. I wanted to use &lt;a href=&quot;https://github.com/dylanaraps/neofetch&quot;&gt;neofetch&lt;/a&gt; to get a nice-looking terminal prompt.
But upon running &lt;code class=&quot;language-text&quot;&gt;brew install neofetch&lt;/code&gt;, I got the following error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Error: Cannot &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; Homebrew on ARM processor &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; Intel default prefix &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/usr/local&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
Please create a new installation &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; /opt/homebrew using one of the
&lt;span class=&quot;token string&quot;&gt;&quot;Alternative Installs&quot;&lt;/span&gt; from:
  https://docs.brew.sh/Installation
You can migrate your previously installed formula list with:
  brew bundle dump&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b3da1adda40ad9134a19b329d479a87d/6ee58/homebrew-arm-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABgklEQVQoz3WRXXOaQBSGSW0Sm0ZQ0NgPTW46Kh8KCCywoDJVSDvt9Ca/oJf9/z/g6bDETqbTXjzzvufM7ruz52ifpMds4TBfOdy3LG0ebI/7lct86eAmGYEsWUWC2cLm41+ce7OlwyLYog2zGmv3iJFVmD9+oT/+ZBBJrKJGF5XCSCs0J/k/rkBbxYyLGu3D52+8r74yP35n1jwxb56Y7hpukwM30Z7eJufCS7tLKkD8O9COuSsbNCPeM4h26MmeYbLHTA8YyYG+n6M5MT1X0PNEp67g1bn2XvS9LvBdGzhZJ1hhjrEWjPwUfZ0oncYlVpAxDnPuokKdMYOM4UYoP37GCjo1NoLN4YQ2bWcXlYy2hWK4LVRthJK3m5SBn/3p6UGOHkj0UD77nGs3VlzaEQ/lCW1c1moBZnFismsw5Ynp/gsjeeRWVFwGhZrja18qPXOxzrrZnbFjxu2XzfyotjMpa1rfDwtuoh1vtqXyrV4Fkn5YcuVL5a/bR9rA80JeLOU3EZ/q8MKkyoAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)&quot;
        title=&quot;Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)&quot;
        src=&quot;/static/b3da1adda40ad9134a19b329d479a87d/d9199/homebrew-arm-01.png&quot;
        srcset=&quot;/static/b3da1adda40ad9134a19b329d479a87d/8ff5a/homebrew-arm-01.png 240w,
/static/b3da1adda40ad9134a19b329d479a87d/e85cb/homebrew-arm-01.png 480w,
/static/b3da1adda40ad9134a19b329d479a87d/d9199/homebrew-arm-01.png 960w,
/static/b3da1adda40ad9134a19b329d479a87d/07a9c/homebrew-arm-01.png 1440w,
/static/b3da1adda40ad9134a19b329d479a87d/6ee58/homebrew-arm-01.png 1640w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Apparently, this happens because I am using a Mac M1 with an ARM processor. As explained on &lt;a href=&quot;https://stackoverflow.com/a/65398385/3375906&quot;&gt;stackoverflow&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Homebrew needs to be installed in two places on Apple silicon:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/usr/local&lt;/code&gt; for rosetta-emulated (Intel) code,&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/opt/homebrew&lt;/code&gt; for ARM64 (M1).&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;These are somewhat hard-coded and the &lt;code class=&quot;language-text&quot;&gt;/opt/homebrew&lt;/code&gt; one MUST be used for ARM code and is &lt;a href=&quot;https://github.com/Homebrew/brew/issues/9130#issuecomment-731581819&quot;&gt;non-negotiable&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;how-i-solved-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-i-solved-it&quot; aria-label=&quot;how i solved it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How I solved it&lt;/h2&gt;
&lt;p&gt;Following those steps below fixes the error for me.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Rosetta2&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/usr/sbin/softwareupdate --install-rosetta --agree-to-license&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Install Homebrew for ARM M1 chip&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;arch &lt;span class=&quot;token parameter variable&quot;&gt;-x86_64&lt;/span&gt; /bin/bash &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Homebrew/install/master/install.sh&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Once installed homebrew for M1 ARM, use this Homebrew command to install packages. Noted the prefix &lt;code class=&quot;language-text&quot;&gt;arch -x86_64&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;arch &lt;span class=&quot;token parameter variable&quot;&gt;-x86_64&lt;/span&gt; brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;package&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I ran the following for my use case above for &lt;code class=&quot;language-text&quot;&gt;neofetch&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;arch &lt;span class=&quot;token parameter variable&quot;&gt;-x86_64&lt;/span&gt; brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; neofetch&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I can then install &lt;code class=&quot;language-text&quot;&gt;neofetch&lt;/code&gt; as expected.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4f6afcb309aec86e01fc617af8bc8c45/6408a/homebrew-arm-02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIAAf/aAAwDAQACEAMQAAABzkoQogs//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAwADAAAAAAAAAAAAAAAAAAEREDFh/9oACAEBAAE/IRwc4PQ3j//aAAwDAQACAAMAAAAQAD//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAcEAACAgIDAAAAAAAAAAAAAAAAAREhMUFhkaH/2gAIAQEAAT8QeOBlzAzun0Y8Sd3BLr0R/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)&quot;
        title=&quot;Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)&quot;
        src=&quot;/static/4f6afcb309aec86e01fc617af8bc8c45/6a068/homebrew-arm-02.jpg&quot;
        srcset=&quot;/static/4f6afcb309aec86e01fc617af8bc8c45/09b79/homebrew-arm-02.jpg 240w,
/static/4f6afcb309aec86e01fc617af8bc8c45/7cc5e/homebrew-arm-02.jpg 480w,
/static/4f6afcb309aec86e01fc617af8bc8c45/6a068/homebrew-arm-02.jpg 960w,
/static/4f6afcb309aec86e01fc617af8bc8c45/644c5/homebrew-arm-02.jpg 1440w,
/static/4f6afcb309aec86e01fc617af8bc8c45/6408a/homebrew-arm-02.jpg 1626w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#next-steps&quot; aria-label=&quot;next steps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;In the &lt;a href=&quot;https://github.com/Homebrew/brew/issues/9130#issuecomment-731581819&quot;&gt;comment&lt;/a&gt;, &lt;code class=&quot;language-text&quot;&gt;rahul&lt;/code&gt; suggested that you should add an alias to your &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; file for future convenience.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;brew86&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arch -x86_64 /usr/local/homebrew/bin/brew&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Therefore the above install command will become&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; arch -x86_64 brew install neofetch
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; brew86 install neofetch&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;⚠️ Because &lt;code class=&quot;language-text&quot;&gt;zsh&lt;/code&gt; runs the binary in &lt;code class=&quot;language-text&quot;&gt;/usr/local/bin&lt;/code&gt; by default. Running &lt;code class=&quot;language-text&quot;&gt;brew86&lt;/code&gt; will probably change the location newly installed application to &lt;code class=&quot;language-text&quot;&gt;/usr/local/homebrew/bin/brew&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You must also add the following to &lt;code class=&quot;language-text&quot;&gt;.zshrc&lt;/code&gt; so that &lt;code class=&quot;language-text&quot;&gt;zsh&lt;/code&gt; recognizes the binary in the new location.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;&lt;span class=&quot;token environment constant&quot;&gt;PATH&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/usr/local/homebrew/bin:&lt;span class=&quot;token environment constant&quot;&gt;$PATH&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Otherwise, you’ll see some errors like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;zsh: &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; not found: pyenv&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 632px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d32f7f5367792e1aae1def5fb2398f38/84f0d/homebrew-arm-03.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAFABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAMEBf/EABcBAAMBAAAAAAAAAAAAAAAAAAABAgP/2gAMAwEAAhADEAAAAc2QvOuEf//EABcQAQADAAAAAAAAAAAAAAAAAAEAETH/2gAIAQEAAQUCgWO//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8BZ//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/ASf/xAAWEAADAAAAAAAAAAAAAAAAAAAAARD/2gAIAQEABj8CHP/EABkQAAIDAQAAAAAAAAAAAAAAAAABESExUf/aAAgBAQABPyFIjU5eFXXGf//aAAwDAQACAAMAAAAQC/8A/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAh/9oACAEDAQE/EIC//8QAFhEAAwAAAAAAAAAAAAAAAAAAARAh/9oACAECAQE/ELK//8QAHBABAAIBBQAAAAAAAAAAAAAAAQAxESFBUWHw/9oACAEBAAE/EMvGvUR4Adq/EAEWE//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)&quot;
        title=&quot;Error: Cannot install in Homebrew on ARM processor in Intel default prefix (/usr/local)&quot;
        src=&quot;/static/d32f7f5367792e1aae1def5fb2398f38/84f0d/homebrew-arm-03.jpg&quot;
        srcset=&quot;/static/d32f7f5367792e1aae1def5fb2398f38/09b79/homebrew-arm-03.jpg 240w,
/static/d32f7f5367792e1aae1def5fb2398f38/7cc5e/homebrew-arm-03.jpg 480w,
/static/d32f7f5367792e1aae1def5fb2398f38/84f0d/homebrew-arm-03.jpg 632w&quot;
        sizes=&quot;(max-width: 632px) 100vw, 632px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[/usr/local/bin/code: line 6: python: command not found]]></title><description><![CDATA[How to fix this error when trying to run code . on MacOS]]></description><link>https://trungvose.combin-code-line-6-python-command-not-found/</link><guid isPermaLink="false">https://trungvose.combin-code-line-6-python-command-not-found/</guid><pubDate>Tue, 19 Apr 2022 13:45:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, when trying to run &lt;code class=&quot;language-text&quot;&gt;code .&lt;/code&gt; from terminal, I got the following error:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;/usr/local/bin/code: line &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;: python: &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; not found
/usr/local/bin/code: line &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;: ./MacOS/Electron: No such &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; or directory&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/57b5bd2e90020c12c36a9b67fdac1fd6/dca52/vscode-python-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 23.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAxUlEQVQY022Q5wqEMBCE8xz2hhobNmzYwPd/pjlmIXIc9+NjS5LZzKpt21CWJaIokqi1lhgEAfI8R1VVqOtaSJJEznzfh+d5f1FFUchFXorjWIQJBYnjOHBdF7ZtC6wty3r7v6jrurCuK87zxPM8YL0si/TmecYwDPI79tq2lbzve6RpKgPDMHzhp9RxHPJ433fc9y2Ra+AAWqbQNE0YxxFd14koBbkKuuOKOKRpGnGosiwTdRbMaZ9CzI09Y/cbY5nRQMsfc3mSYoHdvkIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;/usr/local/bin/code: line 6: python: command not found&quot;
        title=&quot;/usr/local/bin/code: line 6: python: command not found&quot;
        src=&quot;/static/57b5bd2e90020c12c36a9b67fdac1fd6/d9199/vscode-python-01.png&quot;
        srcset=&quot;/static/57b5bd2e90020c12c36a9b67fdac1fd6/8ff5a/vscode-python-01.png 240w,
/static/57b5bd2e90020c12c36a9b67fdac1fd6/e85cb/vscode-python-01.png 480w,
/static/57b5bd2e90020c12c36a9b67fdac1fd6/d9199/vscode-python-01.png 960w,
/static/57b5bd2e90020c12c36a9b67fdac1fd6/07a9c/vscode-python-01.png 1440w,
/static/57b5bd2e90020c12c36a9b67fdac1fd6/dca52/vscode-python-01.png 1550w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After searching on google, I found that Github issue &lt;a href=&quot;https://github.com/microsoft/vscode/issues/141738&quot;&gt;#141738&lt;/a&gt;. The problem was Apple removed support for Python 2 on macOS 12.3. Therefore when &lt;code class=&quot;language-text&quot;&gt;code&lt;/code&gt; is trying to run &lt;code class=&quot;language-text&quot;&gt;python&lt;/code&gt; command, it will fail.&lt;/p&gt;
&lt;p&gt;To fix the issue, do the following steps.&lt;/p&gt;
&lt;p&gt;I got mine working by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cd /usr/local/bin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;nano code&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;change &lt;code class=&quot;language-text&quot;&gt;python&lt;/code&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;python3&lt;/code&gt; on the following line&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; function realpath() { python -c ...
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; function realpath() { python3 -c ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To save the file on nano, press &lt;code class=&quot;language-text&quot;&gt;Ctrl + Option + o&lt;/code&gt;. Restart the terminal, and now &lt;code class=&quot;language-text&quot;&gt;code .&lt;/code&gt; works!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bda54d1354342570ec836af4f55dddc5/6e52c/vscode-python-02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB6UlEQVQozz2SWY/aQBCE/ScACZbDsNy2IRzGNkHZXQw2dxICzzzAEhGEhCBAomz+eiXVK/NQ6p6e6Zmab0a5nr7jdb/FZr3B4XDA9XrF7XaT+OvPG36//cXP8xnn81nqgYJ1l8sF6/UG+/0PTKYzKLPPXzD/Osf82xzL5RKLxQLD4RDj8Rij0QgD30e/30ev14Pnefc643Q6xWQykXn22LYDxTAM6LqOYrGIfD4vMa2qUFMpPD4+QlVVpFIpiZlMRnLGWCyGUCgkikQiCIfDeHiIQ2k0Gmi322BsNptoNk3UmyYaZgu240iNc61WC6Zpol6vS846zZTLZRFzTdehcLJWq0mTZduoVgw8m1VMniw8f7TxoVaDZVmyhu7pLB6Pi5LJ5F3vt0hD4d193xcNBgN0u1147gtGnouXp09wHEecUdxU0zRRtVq9OwtwEZHiuq6ADWDzALfXw2g8huf5goPNZJjNZlEoFJDL5WQDjrlJoGKpBIUNtm3feZBRqVSCYeioVCpSowM+hHDSNJnnQzLyANbfeypQ2JBIJJBOp6WRbDhmTjYcM6ebaDQqTuiOObkFPbFoFNlsDsp+vxcnBH86neRPkdfxeMRsNpPX3G63WK1W4or/dLfbyU2IiR++0+nAtixk/jP8B62ST6cF15UPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;/usr/local/bin/code: line 6: python: command not found&quot;
        title=&quot;/usr/local/bin/code: line 6: python: command not found&quot;
        src=&quot;/static/bda54d1354342570ec836af4f55dddc5/d9199/vscode-python-02.png&quot;
        srcset=&quot;/static/bda54d1354342570ec836af4f55dddc5/8ff5a/vscode-python-02.png 240w,
/static/bda54d1354342570ec836af4f55dddc5/e85cb/vscode-python-02.png 480w,
/static/bda54d1354342570ec836af4f55dddc5/d9199/vscode-python-02.png 960w,
/static/bda54d1354342570ec836af4f55dddc5/07a9c/vscode-python-02.png 1440w,
/static/bda54d1354342570ec836af4f55dddc5/29114/vscode-python-02.png 1920w,
/static/bda54d1354342570ec836af4f55dddc5/6e52c/vscode-python-02.png 1948w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Multiple ng-content]]></title><description><![CDATA[How to reuse multiple unamed ng-content in Angular]]></description><link>https://trungvose.commultiple-ng-content/</link><guid isPermaLink="false">https://trungvose.commultiple-ng-content/</guid><pubDate>Sun, 10 Apr 2022 03:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;I have a component with multiple &lt;code class=&quot;language-text&quot;&gt;ng-content&lt;/code&gt;. Basically if you passed a &lt;code class=&quot;language-text&quot;&gt;routerLink&lt;/code&gt; Input into the component, it will render &lt;code class=&quot;language-text&quot;&gt;&amp;lt;a&gt;&lt;/code&gt; tag, otherwise it will render &lt;code class=&quot;language-text&quot;&gt;&amp;lt;button&gt;&lt;/code&gt; tag. Obviously it is not how you design a link component but it is a good example. I actually did it around 2016 when I was learning Angular 😂&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app-link&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    &amp;lt;a *ngIf=&quot;routerLink&quot; [routerLink]=&quot;routerLink&quot;&gt;
      {{ routerLink }} - &amp;lt;ng-content&gt;&amp;lt;/ng-content&gt;
    &amp;lt;/a&gt;
    &amp;lt;button *ngIf=&quot;!routerLink&quot;&gt;
      &amp;lt;ng-content&gt;&amp;lt;/ng-content&gt;
    &amp;lt;/button&gt;
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  styles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LinkComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; routerLink&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And you would probably notice that the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;a&gt;&lt;/code&gt; tag was render but nothing come has been projected into &lt;code class=&quot;language-text&quot;&gt;&amp;lt;ng-content&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;routerLink&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;My link&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;My button&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result come, but only the &lt;code class=&quot;language-text&quot;&gt;My button&lt;/code&gt; text get projected.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e622c476e74a5dd02635ecc7f0c80e88/082c8/multiple-ng-content-01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAABD0lEQVQY032Oy0rDUBRFMxdEMTY3msdNk9gmrTWoWFTEKo78ARF89qXfnAy0dVqwoEILTZYkitaJGxZsDpzFVrxGk2h7j2inSRjWCesRlTCi7AUYtldgSg/pVtmMdrm4vOL6ts3NXZtu/5F2t899p0en91DcFKFbmG4Nu7KFW23g+wGGW2NdbmBZZSzp47hVVGHRap2RJDFJkjAcDonjmNFoRJ7ZbEaaZigl3UaYProMkG6A6VSKLowymm6jrUkcy8HUBOetY54Gr7y9fzCdTplMJoVoPsqqsFA1A7VksPJN3lXN/EXYLCyV2D884Xnwwng8/iPJsuwHJX/Ipf+RL11c1jg4OiVNvxalc5J56SfVGeBWgC/AcQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Multiple ng-content&quot;
        title=&quot;Multiple ng-content&quot;
        src=&quot;/static/e622c476e74a5dd02635ecc7f0c80e88/d9199/multiple-ng-content-01.png&quot;
        srcset=&quot;/static/e622c476e74a5dd02635ecc7f0c80e88/8ff5a/multiple-ng-content-01.png 240w,
/static/e622c476e74a5dd02635ecc7f0c80e88/e85cb/multiple-ng-content-01.png 480w,
/static/e622c476e74a5dd02635ecc7f0c80e88/d9199/multiple-ng-content-01.png 960w,
/static/e622c476e74a5dd02635ecc7f0c80e88/07a9c/multiple-ng-content-01.png 1440w,
/static/e622c476e74a5dd02635ecc7f0c80e88/29114/multiple-ng-content-01.png 1920w,
/static/e622c476e74a5dd02635ecc7f0c80e88/082c8/multiple-ng-content-01.png 2734w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There are several issues has been created on the angular repository regarding this issue, for example &lt;a href=&quot;https://github.com/angular/angular/issues/9173&quot;&gt;#9173&lt;/a&gt; and &lt;a href=&quot;https://github.com/angular/angular/issues/22972&quot;&gt;#22972&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;Why it is working this way, I don’t think I can explain in an easy way for you. Therefore feel free to check &lt;a href=&quot;https://github.com/angular/angular/issues/9173#issuecomment-225732419&quot;&gt;misko’s detailed comment&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One notable workaround can be found on &lt;a href=&quot;https://github.com/angular/angular/issues/22972#issuecomment-407358396&quot;&gt;#22972 nelisbijl’ comment&lt;/a&gt;. Essentially, you don’t try to render multiple ng-content, but putting &lt;code class=&quot;language-text&quot;&gt;ng-content&lt;/code&gt; inside an &lt;code class=&quot;language-text&quot;&gt;ng-template&lt;/code&gt; and render that template inside your &lt;code class=&quot;language-text&quot;&gt;ngIf&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app-link-fixed&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    &amp;lt;a *ngIf=&quot;routerLink&quot; [routerLink]=&quot;routerLink&quot;&gt;
      {{ routerLink }} -
      &amp;lt;ng-container *ngTemplateOutlet=&quot;contentTmpl&quot;&gt;&amp;lt;/ng-container&gt;
    &amp;lt;/a&gt;
    &amp;lt;button *ngIf=&quot;!routerLink&quot;&gt;
      &amp;lt;ng-container *ngTemplateOutlet=&quot;contentTmpl&quot;&gt;&amp;lt;/ng-container&gt;
    &amp;lt;/button&gt;
    &amp;lt;ng-template #contentTmpl&gt;
      &amp;lt;ng-content&gt;&amp;lt;/ng-content&gt;
    &amp;lt;/ng-template&gt;
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  styles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      :host {
        display: block;
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LinkFixedComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; routerLink&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8af1aa5846661db54ee4060a8ae5cc17/ea9a1/multiple-ng-content-02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABXElEQVQoz42Q30/aYBSGe6VR4wJT7O9+7ddCS5FRTefF/hgCIhimM/6/6EiWApkjW2KicoF9lpJItnjDSd68OTfPed+j+I2UZivl08kZzUbCSfKZsJEg/Rp+NUZWY4Ss40YJrgxJz75wfjGg2xvQHwy5HF4xvPrG9c3typUjzcaSMTJqEURNLDdEtQM0w0XVBUe6oKK7q317t0S70+P7/R2j0YjJZEKWZfw7imq4WH4TGZ8Sxi0cv4Hp1TEsiWVLTCdAdWqols/O/gHtTp9f8z/MZjOWy+UKkuf5WopmeogwwYtTguOUqIDKOnaYYIsqpi3xvAAhI3bLOu3uVx7mz+T56zvYCqhbHkXKolbhuumhvskQVDQH3XCoCUGlVObivMd0mjEej1ksFmvomyuHqs0mKg6WP5S47veZ/lzwI3v8r/L6h0WCTaSZLlt7H2l3L5nPf/P09PIOVgD/AoJjMwy99PVsAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Multiple ng-content&quot;
        title=&quot;Multiple ng-content&quot;
        src=&quot;/static/8af1aa5846661db54ee4060a8ae5cc17/d9199/multiple-ng-content-02.png&quot;
        srcset=&quot;/static/8af1aa5846661db54ee4060a8ae5cc17/8ff5a/multiple-ng-content-02.png 240w,
/static/8af1aa5846661db54ee4060a8ae5cc17/e85cb/multiple-ng-content-02.png 480w,
/static/8af1aa5846661db54ee4060a8ae5cc17/d9199/multiple-ng-content-02.png 960w,
/static/8af1aa5846661db54ee4060a8ae5cc17/07a9c/multiple-ng-content-02.png 1440w,
/static/8af1aa5846661db54ee4060a8ae5cc17/29114/multiple-ng-content-02.png 1920w,
/static/8af1aa5846661db54ee4060a8ae5cc17/ea9a1/multiple-ng-content-02.png 2970w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/multiple-ng-content-ivy?file=src/app/link-fix.component.ts&quot;&gt;&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[My React Reading List]]></title><description><![CDATA[My collection of React articles and video that I read and enjoyed.]]></description><link>https://trungvose.comreact-reading-list/</link><guid isPermaLink="false">https://trungvose.comreact-reading-list/</guid><pubDate>Sun, 12 Dec 2021 14:45:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been working mainly with Angular in the previous few years and now moved to React for about a year. The following is my React reading list that I have collected over the past year, hope you enjoy it as much as I enjoyed reading and watching them.&lt;/p&gt;
&lt;p&gt;View the &lt;a href=&quot;https://trungvose.notion.site/ae0112f417654a56bed63372edecfde6?v=ec9d3a6cffd04d57af03e5a67c483aea&quot;&gt;full list here on notion&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Slick - prevent layout shift for your slider]]></title><description><![CDATA[Slick has been my top choice when it comes to implementing feature-rich sliders on the web]]></description><link>https://trungvose.comslick-layout-shift/</link><guid isPermaLink="false">https://trungvose.comslick-layout-shift/</guid><pubDate>Sat, 18 Sep 2021 04:45:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I revamped our new website with a completely new catchy design. It has many sliders, which is a very popular feature of most websites, and I wanted to implement it in a way that would not cause layout shifts. I choose Slick as the library to implement the slider.&lt;/p&gt;
&lt;p&gt;All I need to do is include the library with a &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt; tag, the required CSS from Slick and initialize it with a simple JavaScript.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.js-slider&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;arrow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;dots&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;HTML code would look like&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;js-slider&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;slide&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://via.placeholder.com/350x300?text=1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;slide&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://via.placeholder.com/350x300?text=2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;slide&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://via.placeholder.com/350x300?text=3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that all images will be rendered first before the JS kicks in to initialize the slider that could cause a layout shift, as shown in the screenshot below. Locally, we usually don’t need to worry about the layout shift, but it is a must for our end users.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/652e226a09130deb3489d9e6a9ab05c0/slick-pen-before.gif&quot; alt=&quot;Slick - prevent layout shift for your slider&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;I could do a pretty easy fix with just a few lines of CSS. We need to ensure our images have the same styles before and after the scripts are parsed and executed. In this case, all it takes is the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;
&lt;span class=&quot;token selector&quot;&gt;.js-slider &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  
  &amp;amp; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; .&lt;span class=&quot;token property&quot;&gt;slide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;first-child&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This way, we hide everything but the first slide in our carousel and add the padding on the sides that Slick will add after it loads. Slick will add extra wrapping elements to our slides, so the immediate children selector (&lt;code class=&quot;language-text&quot;&gt;&gt;&lt;/code&gt;) will make the styles only apply before it loads.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/134f8e70dc2d6ae6e4ab4a570cb44a13/slick-pen-after.gif&quot; alt=&quot;Slick - prevent layout shift for your slider&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notice that the &lt;code class=&quot;language-text&quot;&gt;$(window).on(&quot;load&quot;&lt;/code&gt; is important because if you use &lt;code class=&quot;language-text&quot;&gt;$(document).ready()&lt;/code&gt; it will not work as expected. &lt;code class=&quot;language-text&quot;&gt;$(window).on(&quot;load&quot;&lt;/code&gt; will make sure the images are rendered before the slider is initialized.&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;iframe height=&quot;300&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Slick - prevent layout shift for your slider&quot; src=&quot;https://codepen.io/trungvose/embed/oNwEJej?default-tab=html%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&quot;https://codepen.io/trungvose/pen/oNwEJej&quot;&gt;
  Slick - prevent layout shift for your slider&lt;/a&gt; by Trung Vo (&lt;a href=&quot;https://codepen.io/trungvose&quot;&gt;@trungk18&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.codementor.io/@michelre/using-box-shadow-to-construct-a-border-ex0rpxvng&quot;&gt;https://www.codementor.io/@michelre/using-box-shadow-to-construct-a-border-ex0rpxvng&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[CSS div jumped when adding a border]]></title><description><![CDATA[When hovering into a div, we usually want to show that this div is highlighted, usually with different border.]]></description><link>https://trungvose.comcss-div-jump-border/</link><guid isPermaLink="false">https://trungvose.comcss-div-jump-border/</guid><pubDate>Sat, 21 Aug 2021 03:45:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/c5390ed53fb58aa43373f015369daf9d/border-shadow-03.gif&quot; alt=&quot;CSS div jumped when adding a border&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;TL;DR:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using &lt;code class=&quot;language-text&quot;&gt;box-shadow&lt;/code&gt; to highlight the div could be a good idea instead of using border.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;adding-a-border-to-a-div&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#adding-a-border-to-a-div&quot; aria-label=&quot;adding a border to a div permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Adding a border to a div&lt;/h2&gt;
&lt;p&gt;So, adding a border to a box, is pretty simple when you use the &lt;code class=&quot;language-text&quot;&gt;border&lt;/code&gt; property. Let’s start a first basic example by drawing a small square and adding a red border to it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;box border&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Border&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.box&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.border&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3px solid red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/6b4d93f263b2ca76fa6bdd3b21aaa655/border-shadow-01.gif&quot; alt=&quot;CSS div jumped when adding a border&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Do you see what’s happening when your mouse is hovering the box? Your content move forward and the rendering is not really great.&lt;/p&gt;
&lt;p&gt;If the normal border is 3px and when hover, it is also 3px. You can fix it with a simple trick to set the initial border to &lt;code class=&quot;language-text&quot;&gt;3px transparent&lt;/code&gt; and then set the final border to &lt;code class=&quot;language-text&quot;&gt;3px red&lt;/code&gt;. But here, we have initial of &lt;code class=&quot;language-text&quot;&gt;1px&lt;/code&gt; and on hover &lt;code class=&quot;language-text&quot;&gt;3px&lt;/code&gt;. That’s came to the solution below where we use &lt;code class=&quot;language-text&quot;&gt;box-shadow&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;border&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;using-box-shadow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-box-shadow&quot; aria-label=&quot;using box shadow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using box-shadow&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inset | offset-x | offset-y | blur-radius | spread-radius | color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obviously, &lt;code class=&quot;language-text&quot;&gt;box-shadow&lt;/code&gt; is a property that is used to add shadow to a box. But we can utilize it to add a border to a box as well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 1px 0 0 red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Border bottom */&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 -1px 0 0 red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Border top */&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1px 0 0 0 red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Border left */&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px 0 0 0 red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Border right */&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 0 1px red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* All the borders by using the spread properties */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So with that in mind, we can now use the box-shadow to resolve our previous issue. So I added a new box and drawn the borders with the box-shadow property:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/84928de496ee6ace5925693e48d16687/border-shadow-02.gif&quot; alt=&quot;CSS div jumped when adding a border&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;box shadow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Box shadow&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.shadow&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 0 1px red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 0 3px red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;iframe height=&quot;300&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Border or box-shadow&quot; src=&quot;https://codepen.io/trungvose/embed/GREKgpN?default-tab=html%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&quot;https://codepen.io/trungvose/pen/GREKgpN&quot;&gt;
  Border or box-shadow&lt;/a&gt; by Trung Vo (&lt;a href=&quot;https://codepen.io/trungvose&quot;&gt;@trungk18&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.codementor.io/@michelre/using-box-shadow-to-construct-a-border-ex0rpxvng&quot;&gt;https://www.codementor.io/@michelre/using-box-shadow-to-construct-a-border-ex0rpxvng&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Align React Material UI Dialog to the top instead of center]]></title><description><![CDATA[We need to override two custom classes for Material Dialog component always to have it stick to the top.]]></description><link>https://trungvose.comreact-material-ui-dialog-position/</link><guid isPermaLink="false">https://trungvose.comreact-material-ui-dialog-position/</guid><pubDate>Sat, 10 Jul 2021 13:45:00 GMT</pubDate><content:encoded>&lt;p&gt;As you work with Material Dialog, you should know that its default position is always to the center of the page. But more often, we want to make the dialog stick to the top instead.&lt;/p&gt;
&lt;h3 id=&quot;1-for-scrollpaper&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-for-scrollpaper&quot; aria-label=&quot;1 for scrollpaper permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. For &lt;code class=&quot;language-text&quot;&gt;scroll=&apos;paper&apos;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;By default, the dialog alignment styles were inside the &lt;code class=&quot;language-text&quot;&gt;.MuiDialog-scrollPaper&lt;/code&gt; class with &lt;code class=&quot;language-text&quot;&gt;display: flex&lt;/code&gt;. So to easily change the position to the top, we only need to adjust &lt;code class=&quot;language-text&quot;&gt;align-items&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;flex-start&lt;/code&gt; and voila, the dialog now is displaying on the top as we expected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/684db9fffa5c4873cdb05b0a4d33c15f/paper.gif&quot; alt=&quot;Align React Material UI Dialog to the top instead of center&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-for-scrollbody&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-for-scrollbody&quot; aria-label=&quot;2 for scrollbody permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. For &lt;code class=&quot;language-text&quot;&gt;scroll=&apos;body&apos;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;.MuiDialog-paperScrollBody&lt;/code&gt; class with &lt;code class=&quot;language-text&quot;&gt;display: inline-block&lt;/code&gt; define the dialog position center. We need to adjust the &lt;code class=&quot;language-text&quot;&gt;vertical-align&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;top&lt;/code&gt; to make the dialog stay at the top.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/eb9dc8ae7d3269d4187e6d5a87d7cc70/body.gif&quot; alt=&quot;Align React Material UI Dialog to the top instead of center&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Don’t use position: absolute or fixed because it will mess up the scroll behavior of the dialog&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To always make the Material Dialog stay at the top of the page, you need to customize both class &lt;code class=&quot;language-text&quot;&gt;scrollPaper&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;paperScrollBody&lt;/code&gt;, as I mentioned above. The code will look like&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; useStyles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeStyles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  topScrollPaper&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    alignItems&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;flex-start&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  topPaperScrollBody&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    verticalAlign&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;top&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SimpleDialog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SimpleDialogProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; classes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useStyles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Dialog&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onClose&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;handleClose&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;aria-labelledby&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;simple-dialog-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;open&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;scroll&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;paper&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;classes&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        scrollPaper&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; classes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topScrollPaper&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        paperScrollBody&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; classes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topPaperScrollBody&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Dialog&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#demo&quot; aria-label=&quot;demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Demo&lt;/h3&gt;
&lt;p&gt;View on &lt;a href=&quot;https://codesandbox.io/s/react-material-dialog-position-4xwq2?file=/dialog.tsx&quot;&gt;codesanbox&lt;/a&gt;&lt;/p&gt;
&lt;iframe src=&quot;https://codesandbox.io/embed/react-material-dialog-position-4xwq2?fontsize=14&amp;hidenavigation=1&amp;module=%2Fdialog.tsx&amp;moduleview=1&amp;theme=dark&quot;
     style=&quot;width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;&quot;
     title=&quot;React Material Dialog Position&quot;
     allow=&quot;accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking&quot;
     sandbox=&quot;allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts&quot;
   &gt;&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[TypeScript - Property 'onerror' does not exist on type 'EventTarget']]></title><description><![CDATA[How to fix this kind of issue - Property 'onerror' does not exist on type 'EventTarget']]></description><link>https://trungvose.comonerror-typescript/</link><guid isPermaLink="false">https://trungvose.comonerror-typescript/</guid><pubDate>Sat, 03 Jul 2021 10:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Recently, I converted one legacy React component to TS, and this error starts to appear.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Property &apos;onerror&apos; does not exist on type &apos;EventTarget&apos;&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;vip-badge-avatar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;md5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;?size=80&amp;amp;d=blank&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;onError&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onerror &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userDefaultPicture&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Profile Avatar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/22ed7f679d2da27026c0562dc04609df/f470a/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABGklEQVQoz42QWWrDQBAFdQ9LGo22WWRptIy8SDY4BpMEfP/rVMiYJAQC8UdR/frj0XSU2x5ZK6TMKDJBKUVwLlLiOGaz2fzhTfDXLkmSb0fudGa5vLDMM5fjgeu6cN7vWPY72rZjGEac6+mHga7r6Psh5HEc8d7jnENrE1xVNVHVDcx+YjkutINHK4VSCmMajLFYa7HGBD9yE2iabeBzVkoHh8J2OuAnxzR5mvUNM1/Q4wnt9qh2pupXandAdR6jdSh9YMJlP9lS14rITjvG2x1/fWd/vVOvN8T6SuZPxN0O0U4IWSAyiRDiXyKrK+qyRJUFOhfIXJJkkjjLSWROmknSJAkPf4ZIN1u2VYEpCzpdY8o8FPwiTZ/mAzX7y8nXw/0yAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Property &amp;#39;onerror&amp;#39; does not exist on type &amp;#39;EventTarget&quot;
        title=&quot;Property &amp;#39;onerror&amp;#39; does not exist on type &amp;#39;EventTarget&quot;
        src=&quot;/static/22ed7f679d2da27026c0562dc04609df/d9199/01.png&quot;
        srcset=&quot;/static/22ed7f679d2da27026c0562dc04609df/8ff5a/01.png 240w,
/static/22ed7f679d2da27026c0562dc04609df/e85cb/01.png 480w,
/static/22ed7f679d2da27026c0562dc04609df/d9199/01.png 960w,
/static/22ed7f679d2da27026c0562dc04609df/07a9c/01.png 1440w,
/static/22ed7f679d2da27026c0562dc04609df/f470a/01.png 1443w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;reason&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reason&quot; aria-label=&quot;reason permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reason&lt;/h2&gt;
&lt;p&gt;By default an &lt;code class=&quot;language-text&quot;&gt;event&lt;/code&gt; target element has an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/EventTarget&quot;&gt;EventTarget&lt;/a&gt; type in TypeScript. It has just a few properties that we can use, and most of the time, we will need to assert the event type to the correct type.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;If I assert the type of the event target to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement&quot;&gt;HTMLImageElement&lt;/a&gt;, the TS error went away. The &lt;code class=&quot;language-text&quot;&gt;HTMLImageElement&lt;/code&gt; interface represents an HTML &lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; element, providing the properties and methods used to manipulate image elements.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;vip-badge-avatar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;md5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;?size=80&amp;amp;d=blank&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;onError&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; HTMLImageElement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onerror &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; HTMLImageElement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; userDefaultPicture&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Profile Avatar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[TypeScript data structure: Stack]]></title><description><![CDATA[My implementation for Stack using TypeScript]]></description><link>https://trungvose.comtypescript-stack/</link><guid isPermaLink="false">https://trungvose.comtypescript-stack/</guid><pubDate>Sat, 19 Jun 2021 11:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;stack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stack&quot; aria-label=&quot;stack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stack&lt;/h2&gt;
&lt;p&gt;In computer science, a &lt;strong&gt;stack&lt;/strong&gt; is an abstract data type that serves as a collection of elements, with two principal operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;push&lt;/strong&gt;, which adds an element to the collection, and&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pop&lt;/strong&gt;, which removes the most recently added element that was not yet removed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The order in which elements come off a stack gives rise to its alternative name, LIFO (last in, first out). Additionally, a peek operation may give access to the top without modifying the stack. The name “stack” for this type of structure comes from the analogy to a set of physical items stacked on top of each other, which makes it easy to take an item off the top of the stack, while getting to an item deeper in the stack may require taking off multiple other items first.&lt;/p&gt;
&lt;p&gt;Simple representation of a stack runtime with push and pop operations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/b/b4/Lifo_stack.png&quot; alt=&quot;Stack&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation&quot; aria-label=&quot;implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation&lt;/h2&gt;
&lt;p&gt;Implement using built-in object as a map, with tail pointer. I came up with this approach to make sure the Big O notation for the &lt;code class=&quot;language-text&quot;&gt;pop&lt;/code&gt; operation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Big O&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;push(value)&lt;/td&gt;
&lt;td&gt;adds an element to the collection&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pop()&lt;/td&gt;
&lt;td&gt;removes the most recently added element&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;peek()&lt;/td&gt;
&lt;td&gt;returns the most recently added element without modifying the stack&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clear()&lt;/td&gt;
&lt;td&gt;removes all the element in the stack&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isEmpty&lt;/td&gt;
&lt;td&gt;checks if the stack is empty&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;size&lt;/td&gt;
&lt;td&gt;checks the size of the stack&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;completed-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#completed-source-code&quot; aria-label=&quot;completed source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Completed Source code&lt;/h2&gt;
&lt;p&gt;You can find the source code in my TS data structure repo&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/typescript-data-structures/blob/master/data-structures/stack/stack.ts&quot;&gt;trungk18/typescript-data-structures/tree/master/data-structures/stack&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ObjectType &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../model/objectType&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stack&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _stack&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ObjectType&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_stack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_stack&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_stack&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_stack&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_stack&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_stack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; values &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_stack&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; -&gt; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;alternative-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#alternative-implementation&quot; aria-label=&quot;alternative implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Alternative Implementation&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/davidshariff/computer-science/blob/master/Data%20Structures/Stack.js&quot;&gt;Trivial implementation using Array&lt;/a&gt; - Using &lt;code class=&quot;language-text&quot;&gt;arr.splice()&lt;/code&gt; for pop, could take &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt;. That will not meet the requirement of &lt;code class=&quot;language-text&quot;&gt;O(1)&lt;/code&gt; for pop operation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yangshun/lago/blob/master/lib/data-structures/Stack.js&quot;&gt;Stack&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Stack_(abstract_data_type)&quot;&gt;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[TypeScript data structure: Queue]]></title><description><![CDATA[My implementation for Queue using TypeScript]]></description><link>https://trungvose.comtypescript-queue/</link><guid isPermaLink="false">https://trungvose.comtypescript-queue/</guid><pubDate>Sun, 13 Jun 2021 03:45:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;queue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#queue&quot; aria-label=&quot;queue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Queue&lt;/h2&gt;
&lt;p&gt;In computer science, a &lt;strong&gt;queue&lt;/strong&gt; is a collection of entities that are maintained in a sequence and can be modified by the addition of entities at one end of the sequence and the removal of entities from the other end of the sequence. By convention, the end of the sequence at which elements are added is called the back, &lt;strong&gt;tail&lt;/strong&gt;, or rear of the queue, and the end at which elements are removed is called the &lt;strong&gt;head&lt;/strong&gt; or front of the queue, analogously to the words used when people line up to wait for goods or services.&lt;/p&gt;
&lt;p&gt;The operation of adding an element to the rear of the queue is known as enqueue, and the operation of removing an element from the front is known as dequeue. Other operations may also be allowed, often including a peek or front operation that returns the value of the next element to be dequeued without dequeuing it.&lt;/p&gt;
&lt;p&gt;The operations of a queue make it a first-in-first-out (FIFO) data structure. In a FIFO data structure, the first element added to the queue will be the first one to be removed. This is equivalent to the requirement that once a new element is added, all elements that were added before have to be removed before the new element can be removed. A queue is an example of a linear data structure, or more abstractly a sequential collection&lt;/p&gt;
&lt;p&gt;Representation of a FIFO (first in, first out) queue&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/5/52/Data_Queue.svg&quot; alt=&quot;Queue&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation&quot; aria-label=&quot;implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation&lt;/h2&gt;
&lt;p&gt;Implement using built-in object as a map, with tail pointer. I came up with this approach to make sure the Big O notation for the dequeue operation. You can implement with the same approach using array instead of object. But to take one element out of an array could take &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Big O&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;enqueue(value)&lt;/td&gt;
&lt;td&gt;adds value at tail&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dequeue()&lt;/td&gt;
&lt;td&gt;returns value and removes least recently added element (head)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;peek()&lt;/td&gt;
&lt;td&gt;returns the value of the next element to be dequeued without dequeuing it&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clear()&lt;/td&gt;
&lt;td&gt;removes all the element in the queue&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isEmpty&lt;/td&gt;
&lt;td&gt;checks if the queue is empty&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;size&lt;/td&gt;
&lt;td&gt;checks the size of the queue&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;completed-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#completed-source-code&quot; aria-label=&quot;completed source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Completed Source code&lt;/h2&gt;
&lt;p&gt;You can find the source code in my TS data structure repo&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github1s.com/trungk18/typescript-data-structures/tree/master/data-structures/queue&quot;&gt;trungk18/typescript-data-structures/tree/master/data-structures/queue&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ObjectType &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../model/objectType&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Queue&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _queue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ObjectType&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _head&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _tail&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_queue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tail &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;enqueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_queue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tail&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tail&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;dequeue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_queue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_queue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;peek&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_queue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_queue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEmpty&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; values &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tail&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_queue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; -&gt; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;alternative-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#alternative-implementation&quot; aria-label=&quot;alternative implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Alternative Implementation&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/davidshariff/computer-science/blob/master/Data%20Structures/Queue.js&quot;&gt;Trivial implementation using Array&lt;/a&gt; - Using &lt;code class=&quot;language-text&quot;&gt;arr.shift()&lt;/code&gt; for dequeuing, take &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt; time. That will not meet the requirement of &lt;code class=&quot;language-text&quot;&gt;O(1)&lt;/code&gt; for dequeuing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yangshun/lago/blob/master/lib/data-structures/Queue.js&quot;&gt;Queue&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Queue_(abstract_data_type)&quot;&gt;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[TypeScript data structure: Singly linked list]]></title><description><![CDATA[My implementation for singly linked list using TypeScript]]></description><link>https://trungvose.comtypescript-singly-linked-list/</link><guid isPermaLink="false">https://trungvose.comtypescript-singly-linked-list/</guid><pubDate>Sun, 06 Jun 2021 15:15:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;linked-list&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#linked-list&quot; aria-label=&quot;linked list permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Linked List&lt;/h2&gt;
&lt;p&gt;In computer science, a &lt;strong&gt;linked list&lt;/strong&gt; is a linear collection of data elements, in which linear order is not given by their physical placement in memory. Instead, each element points to the next. It is a data structure consisting of a group of nodes which together represent a sequence. Under the simplest form, each node is composed of data and a reference (in other words, a link) to the next node in the sequence. This structure allows for efficient insertion or removal of elements from any position in the sequence during iteration. More complex variants add additional links, allowing efficient insertion or removal from arbitrary element references. A drawback of linked lists is that access time is linear (and difficult to pipeline). Faster access, such as random access, is not feasible. Arrays have better cache locality as compared to linked lists.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg&quot; alt=&quot;Linked List&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;singly-linked-list&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#singly-linked-list&quot; aria-label=&quot;singly linked list permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Singly Linked List&lt;/h2&gt;
&lt;p&gt;I implemented without the tail pointer.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Big O&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pushFront(value)&lt;/td&gt;
&lt;td&gt;Adds an item to the front of the list&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;popFront()&lt;/td&gt;
&lt;td&gt;Remove front item and return its value&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;front()&lt;/td&gt;
&lt;td&gt;Get value of the front item&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pushBack(value)&lt;/td&gt;
&lt;td&gt;Adds an item to the end of the list&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;popBack()&lt;/td&gt;
&lt;td&gt;Remove front item and return its value&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;back()&lt;/td&gt;
&lt;td&gt;Get value of the end item&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reverse()&lt;/td&gt;
&lt;td&gt;Reverses the list&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;has(value)&lt;/td&gt;
&lt;td&gt;Return boolean if the list has a value&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;remove(value)&lt;/td&gt;
&lt;td&gt;Removes the first item in the list with this value&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isEmpty&lt;/td&gt;
&lt;td&gt;Returns true if empty&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;size&lt;/td&gt;
&lt;td&gt;Returns number of data elements in list&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This is my notebook when implementing the reverse operation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/trungk18/typescript-data-structures/master/assets/singly-linked-list-reverse.jpg&quot; alt=&quot;Singly Linked List Reverse&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;completed-source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#completed-source-code&quot; aria-label=&quot;completed source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Completed Source code&lt;/h2&gt;
&lt;p&gt;You can find the source code in my TS data structure repo&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github1s.com/trungk18/typescript-data-structures/tree/master/data-structures/linked-list&quot;&gt;trungk18/typescript-data-structures/tree/master/data-structures/linked-list&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Comparator &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../model/comparator&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Node&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; val&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; next&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Node&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LinkedList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _head&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Node&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _comparatorFunction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Comparator&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Returns true if empty
   */&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Returns number of data elements in list
   */&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Adds an item to the front of the list
   * Time complexity: O(1)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;pushFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LinkedList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; node &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Remove front item and return its value
   * Time complexity: O(1)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;popFront&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; val &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Get value of the front item
   * Time complexity: O(1)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;front&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Adds an item to the end of the list
   * Time complexity: O(n)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;pushBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LinkedList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Remove front item and return its value
   * Time complexity: O(n)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;popBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; val &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; val &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Get value of the end item
   * Time complexity: O(n)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;back&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Reverses the list
   * Time complexity: O(n)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LinkedList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prev &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prev&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      prev &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prev&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Return boolean if the list has a value
   * Time complexity: O(n)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; hasValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_comparatorFunction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hasValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Removes the first item in the list with this value
   * Return true if delete success.
   * Time complexity: O(n)
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_comparatorFunction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_comparatorFunction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; arr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      curr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; curr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos; -&gt; &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_head &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.coursera.org/lecture/data-structures/singly-linked-lists-kHhgK&quot;&gt;Coursera&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Linked_list&quot;&gt;Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jwasham/coding-interview-university#linked-lists&quot;&gt;coding-interview-university&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Difference between: function Person(){}, var person = Person(), and var person = new Person()?]]></title><description><![CDATA[Have you seen this question before?]]></description><link>https://trungvose.comdifferent-between-function-person/</link><guid isPermaLink="false">https://trungvose.comdifferent-between-function-person/</guid><pubDate>Sat, 29 May 2021 08:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Technically speaking, &lt;code class=&quot;language-text&quot;&gt;function Person(){}&lt;/code&gt; is just a normal function declaration. The convention is to use PascalCase for functions that are intended to be used as constructors.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var person = Person()&lt;/code&gt; invokes the Person as a function, and not as a constructor. Invoking as such is a common mistake if the function is intended to be used as a constructor. Typically, the constructor does not return anything, hence invoking the constructor like a normal function will return undefined and that gets assigned to the variable intended as the instance.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;John&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Uncaught TypeError: Cannot read property &apos;name&apos; of undefined&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var person = new Person()&lt;/code&gt; creates an instance of the Person object using the new operator, which inherits from &lt;code class=&quot;language-text&quot;&gt;Person.prototype&lt;/code&gt;. An alternative would be to use &lt;code class=&quot;language-text&quot;&gt;Object.create&lt;/code&gt;, such as: &lt;code class=&quot;language-text&quot;&gt;Object.create(Person.prototype)&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;John&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Person { name: &quot;John&quot; }&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;john&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new&quot;&gt;Web/JavaScript/Reference/Operators/new&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yangshun/front-end-interview-handbook/blob/master/contents/en/javascript-questions.md#difference-between-function-person-var-person--person-and-var-person--new-person&quot;&gt;yangshun/front-end-interview-handbook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Warning: Can’t perform a React state update on an unmounted component]]></title><description><![CDATA[If you are a react developer, there is a good chance that you faced this warning at least once.]]></description><link>https://trungvose.comcant-perform-react-state-update-on-an-unmounted-component/</link><guid isPermaLink="false">https://trungvose.comcant-perform-react-state-update-on-an-unmounted-component/</guid><pubDate>Sat, 15 May 2021 08:15:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://www.debuggr.io/static/2d12b800cb35cd5e8d89824f3baf60cd/af144/cover.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you are a react developer, there is a good chance that you faced this warning at least once:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;Warning&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Can’t perform a React state update on an unmounted component&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; This &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; a no&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;op&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; but it indicates a memory leak &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; your application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; To fix&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cancel all subscriptions and asynchronous tasks &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; a useEffect cleanup &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I got this issue as well and recently, I found an excellent blog post that explains why it happens and how to fix it.
In short, it is happening when you are updating a component state after a task that takes time to complete. By the time it finishes, you have already shown another component.&lt;/p&gt;
&lt;p&gt;You can see how it is reproduced below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://codesandbox.io/s/github/sag1v/react-async-state-debuggrio&quot;&gt;https://codesandbox.io/s/github/sag1v/react-async-state-debuggrio&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And this is the full blog post from &lt;code class=&quot;language-text&quot;&gt;Sagiv Ben Giat&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.debuggr.io/react-update-unmounted-component/&quot;&gt;https://www.debuggr.io/react-update-unmounted-component/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The proper and reusable fix is introducing a new hook named &lt;code class=&quot;language-text&quot;&gt;useIsMountedRef&lt;/code&gt; to check before you update your component state.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useIsMountedRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isMountedRef &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    isMountedRef&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isMountedRef&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isMountedRef
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Angular - Set page title automatically!]]></title><description><![CDATA[Using a few lines of code could make the application feel much better!]]></description><link>https://trungvose.comangular-auto-set-page-title/</link><guid isPermaLink="false">https://trungvose.comangular-auto-set-page-title/</guid><pubDate>Sat, 01 May 2021 08:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Hey guys, long time no Angular post.&lt;/p&gt;
&lt;p&gt;I have been busy with &lt;a href=&quot;https://github.com/trungvose/angular-spotify&quot;&gt;Angular Spotify&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/angularsg&quot;&gt;Angular Singapore&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today, I’ll share a block of code with you that my friend &lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt; mentioned recently.&lt;/p&gt;
&lt;p&gt;That’s how the output looks like&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/8b8123fcb4a579de3e6538aa2d6fcd5e/set-title.gif&quot; alt=&quot;Angular - auto set page title&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-we-will-do&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-we-will-do&quot; aria-label=&quot;what we will do permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What we will do&lt;/h2&gt;
&lt;p&gt;Our code will basically do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Listen to Router events&lt;/li&gt;
&lt;li&gt;Filter the ResolveEnd events&lt;/li&gt;
&lt;li&gt;Find the deepest activated route (usually is the current activated component)&lt;/li&gt;
&lt;li&gt;Get the data.title that we set up on the router configuration&lt;/li&gt;
&lt;li&gt;Set the HTML document title with the found title.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;First, we need to configure the router with an additional title&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; routes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Routes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; HomeComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Home&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dashboard&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DashboardComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Dashboard&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;On the &lt;code class=&quot;language-text&quot;&gt;app.component.ts&lt;/code&gt;, setup the listener and extract the title&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Angular &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VERSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;major
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; router&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Router&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setupTitleListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupTitleListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;router&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; e &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResolveEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ResolveEnd&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getDeepestChildSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getDeepestChildSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snapshot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ActivatedRouteSnapshot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; deepestChild &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; snapshot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstChild
  &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;deepestChild&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;firstChild&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    deepestChild &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; deepestChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstChild
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; deepestChild &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; snapshot
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Voila, that’s all&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#demo&quot; aria-label=&quot;demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Demo&lt;/h2&gt;
&lt;p&gt;Open &lt;a href=&quot;https://angular-auto-set-page-title.stackblitz.io&quot;&gt;angular-auto-set-page-title.stackblitz.io&lt;/a&gt; to view it on your browser.&lt;/p&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-auto-set-page-title?file=src/app/app.component.ts&quot;&gt;&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[Angular Singapore]]></title><description><![CDATA[Follow @angularsg for the upcoming events :)]]></description><link>https://trungvose.comangular-singapore/</link><guid isPermaLink="false">https://trungvose.comangular-singapore/</guid><pubDate>Sat, 17 Apr 2021 10:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Hey guys,&lt;/p&gt;
&lt;p&gt;Recently I started a new group called Angular Singapore with the target to advocate and grow the Angular developer community in Singapore.&lt;/p&gt;
&lt;h2 id=&quot;activities&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#activities&quot; aria-label=&quot;activities permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Activities&lt;/h2&gt;
&lt;p&gt;Our main activity includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monthly meetup on the first Tuesday of the month. We invite speaker to come and talk about Angular with us. Follow &lt;a href=&quot;https://twitter.com/angularsg&quot;&gt;@angularsg&lt;/a&gt; for the upcoming events :)&lt;/li&gt;
&lt;li&gt;FREE one-on-one support. If you have any questions or need any advices, book our time here. We are here to help!
&lt;a href=&quot;https://calendly.com/trungvose/ama-w-trung&quot;&gt;https://calendly.com/trungvose/ama-w-trung&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/AngularSingapore&quot;&gt;Office-hour&lt;/a&gt; sessions where we share our experience with live coding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is our first meetup photos. I’ll take a good one for the second meetup then 🙂&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/EySsyXDVoAIzNEg?format=jpg&amp;#x26;name=large&quot; alt=&quot;Angular Meetup&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;speak-with-us&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#speak-with-us&quot; aria-label=&quot;speak with us permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Speak with us&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Send us your talk at &lt;a href=&quot;https://twitter.com/angularsg&quot;&gt;@angularsg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;We will get back to you!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;contact-us&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contact-us&quot; aria-label=&quot;contact us permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contact us&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://twitter.com/angularsg&quot;&gt;@angularsg&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Upcoming events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://discord.gg/W5HCQfubJR&quot;&gt;discord.gg/W5HCQfubJR&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Discussion, questions, anything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://calendly.com/trungvose/ama-w-trung&quot;&gt;calendly.com/angular-singapore&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Talk to us, anything!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.youtube.com/AngularSingapore&quot;&gt;youtube.com/AngularSingapore&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Our monthly recorded meetup, office hour and more&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content:encoded></item><item><title><![CDATA[A simple Spotify client built with Angular 11, Nx workspace, ngrx, TailwindCSS and ng-zorro]]></title><description><![CDATA[An example of real-world monorepo codebase using Nx Workspace]]></description><link>https://trungvose.comangular-spotify/</link><guid isPermaLink="false">https://trungvose.comangular-spotify/</guid><pubDate>Wed, 07 Apr 2021 10:15:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;working-application&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-application&quot; aria-label=&quot;working application permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working application&lt;/h2&gt;
&lt;p&gt;Check out the &lt;strong&gt;live application&lt;/strong&gt; -&gt; &lt;a href=&quot;https://spotify.trungk18.com&quot;&gt;https://spotify.trungk18.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spotify premium&lt;/strong&gt; is required for the Web Playback SDK to play music. If you are using a free account, you can still browse the app, but it couldn’t play the music. Sorry about that 🤣&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/angular-spotify-demo-short.gif&quot; alt=&quot;Angular Spotify Demo&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/angular-spotify-visualization.gif&quot; alt=&quot;Angular Spotify Visualizer&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/angular-spotify-browse.gif&quot; alt=&quot;Angular Spotify Browse&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/angular-spotify-album-art.gif&quot; alt=&quot;Angular Spotify Blurry Background&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/angular-spotify-web-player.png&quot; alt=&quot;Angular Spotify Web Player&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;support&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#support&quot; aria-label=&quot;support permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Support&lt;/h2&gt;
&lt;p&gt;If you like my work, feel free to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ this repository. And we will be happy together :)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/intent/tweet?url=https://github.com/trungvose/angular-spotify&amp;#x26;text=A%20cool%20Spotify%20client%20made%20with%20Angular%2011,%20Nx%20workspace,%20ngrx,%20TailwindCSS%20and%20ng-zorro%20%40trungvose&amp;#x26;hashtags=angularspotify,angular,nx,ngrx,ngzorro,typescript&quot;&gt;Tweet&lt;/a&gt; about Angular Spotify&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.buymeacoffee.com/trungvose&quot;&gt;Get me a coffee&lt;/a&gt; ☕️&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks a bunch for stopping by and supporting me!&lt;/p&gt;
&lt;h2 id=&quot;who-is-it-for-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#who-is-it-for-%EF%B8%8F&quot; aria-label=&quot;who is it for ️ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Who is it for 🤷‍♀️&lt;/h2&gt;
&lt;p&gt;I still remember Window Media Player on windows has the visualization when you start to play the music, and I wanted to have the same experience when listening to Spotify. That is the reason I started this project.&lt;/p&gt;
&lt;p&gt;I found that there weren’t many resources on building a proper real-world scale application, and that’s my focus for sharing. I made a &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;Jira clone application&lt;/a&gt; as the first one for that purpose. &lt;a href=&quot;https://nx.dev/&quot;&gt;Nx workspace&lt;/a&gt; is getting more and more attention from the Angular community, but people are always confused about how to architect and set up a Nx project. I hope Angular Spotify will give you more insight on that despite the fact that it is my first project using Nx 🤣&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;You know I am one of the moderators of &lt;a href=&quot;https://twitter.com/ngvnofficial&quot;&gt;Angular Vietnam&lt;/a&gt;. Recently, I also started &lt;a href=&quot;https://twitter.com/angularsg&quot;&gt;Angular Singapore&lt;/a&gt;. This piece of work is my another long-term plan to bring Angular knowledge to more people. I desire to advocate and grow the Angular developer community in Singapore and Vietnam :)&lt;/p&gt;
&lt;h2 id=&quot;tech-stack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tech-stack&quot; aria-label=&quot;tech stack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tech stack&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/angular-spotify-tech-stack.png&quot; alt=&quot;Tech logos&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://angular.io/&quot;&gt;Angular 11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nx.dev/&quot;&gt;Nx Workspace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ngneat&quot;&gt;ngneat&lt;/a&gt; packages includes: &lt;code class=&quot;language-text&quot;&gt;ngneat/svg-icon&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;ngneat/until-destroy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ngrx.io/&quot;&gt;ngrx&lt;/a&gt; and &lt;a href=&quot;https://ngrx.io/guide/component-store&quot;&gt;ngrx/component-store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ng.ant.design/docs/introduce/en&quot;&gt;ng-zorro&lt;/a&gt; UI component: &lt;code class=&quot;language-text&quot;&gt;tooltip&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;modal&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;slider&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;switch&lt;/code&gt; and more.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;TailwindCSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://netlify.com/&quot;&gt;Netlify&lt;/a&gt; for deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I experimented with the ngrx/component store for &lt;code class=&quot;language-text&quot;&gt;AuthStore&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;UIStore&lt;/code&gt;. It might not be a best practice and I will refactor it very soon. Just FYI 🤣&lt;/p&gt;
&lt;h2 id=&quot;high-level-design&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#high-level-design&quot; aria-label=&quot;high level design permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;High-level design&lt;/h2&gt;
&lt;p&gt;See my original notes on &lt;a href=&quot;https://gist.github.com/trungk18/7ef8766cafc05bc8fd87be22de6c5b12&quot;&gt;Nx workspace structure for NestJS and Angular&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;principles&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#principles&quot; aria-label=&quot;principles permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Principles&lt;/h3&gt;
&lt;p&gt;All components are following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OnPush Change Detection and async pipes: all components use observable and async pipe for rendering data without any single manual subscribe. Only some places are calling subscribe for dispatching an action, which I will have a refactor live stream session with my friend &lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt; to use the component store for a fully subscribe-less application.&lt;/li&gt;
&lt;li&gt;SCAMs (single component Angular modules) for tree-shakable components, meaning each component will have a respective module. For example, a RegisterComponent will have a corresponding RegisterModule. We won’t declare RegisterComponent as part of AuthModule, for instance.&lt;/li&gt;
&lt;li&gt;Mostly, everything will stay in the &lt;code class=&quot;language-text&quot;&gt;libs&lt;/code&gt; folder. New modules, new models, new configurations, new components etc… are in libs. libs should be separated into different directories based on existing apps. We won’t put them inside the apps folder. For example in an Angular, contains the &lt;code class=&quot;language-text&quot;&gt;main.ts&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;app.component.ts&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;app.module.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;structure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#structure&quot; aria-label=&quot;structure permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Structure&lt;/h3&gt;
&lt;p&gt;I followed the structure recommended by my friend &lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt;. Below is the simplified version of the application structure.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.
└── root
    ├── apps
    │   └── angular-spotify
    └── libs
        └── web (dir)
            ├── shell (dir)
            │   ├── feature (angular:lib) - for configure any forRoot modules
            │   └── ui
            │       └── layout (angular:lib)
            ├── playlist (dir)
            │   ├── data-access (angular:lib, service, state management)
            │   ├── features
            │   │   ├── list (angular:lib PlaylistsComponent)
            │   │   └── detail (angular:lib PlaylistDetailComopnent)
            │   └── ui (dir)
            │       └── playlist-track (angular:lib, SCAM for Component)
            ├── visualizer (dir)
            │   ├── data-access (angular:lib)
            │   └── feature
            ├── home (dir)
            │   ├── data-access (angular:lib)
            │   ├── feature (angular:lib)
            │   └── ui (dir)
            │       ├── featured-playlists (angular:lib, SCAM for Component)
            │       ├── greeting (angular:lib, SCAM for Component)
            │       └── recent-played (angular:lib, SCAM for Component)
            └── shared (dir)
                ├── app-config (injection token for environment)
                ├── data-access (angular:lib, API call, Service or State management to share across the Client app)
                ├── ui (dir)
                ├── pipes (dir)
                ├── directives (dir)
                └── utils (angular:lib, usually shared Guards, Interceptors, Validators...)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;authentication-flow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#authentication-flow&quot; aria-label=&quot;authentication flow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Authentication Flow&lt;/h3&gt;
&lt;p&gt;I follow &lt;code class=&quot;language-text&quot;&gt;Implicit Grant Flow&lt;/code&gt; that Spotify recommended for client-side only application and does not involve secret keys. The access tokens that are issued are short-lived, and there are no refresh tokens to extend them when they expire.&lt;/p&gt;
&lt;p&gt;View the &lt;a href=&quot;https://developer.spotify.com/documentation/general/guides/authorization-guide/&quot;&gt;Spotify Authorization Guide&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Upon opening Angular Spotify, It will redirect you to Spotify to get access to your data. Angular Spotify only uses the data purely for displaying on the UI. We won’t store your information anywhere else.&lt;/li&gt;
&lt;li&gt;Angular Spotify only keeps the access token in the browser memory without even storing it into browser local storage. If you do a hard refresh on the browser, It will ask for a new access token from Spotify. One access token is only valid for &lt;strong&gt;one hour&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;After having the token, I’ll try to connect to the Web Playback SDK with a new player - &lt;code class=&quot;language-text&quot;&gt;Angular Spotify Web Player&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/sdk-flow.png&quot; alt=&quot;Angular Spotify Web Playback SDK flow&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;dependency-graph&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dependency-graph&quot; aria-label=&quot;dependency graph permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dependency Graph&lt;/h3&gt;
&lt;p&gt;Nx provides an &lt;a href=&quot;https://nx.dev/latest/angular/structure/dependency-graph&quot;&gt;dependency graph&lt;/a&gt; out of the box. To view it on your browser, clone my repository and run:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run dep-graph&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A simplified graph looks like the following. It gives you insightful information for your mono repo and especially helpful when multiple projects are depending on each other.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/dep-graph.png&quot; alt=&quot;Angular Spotify Dependency Graph&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;nx-computation-cache&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nx-computation-cache&quot; aria-label=&quot;nx computation cache permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nx Computation Cache&lt;/h3&gt;
&lt;p&gt;Having Nx Cloud configured shorten the deployment time quite a lot.&lt;/p&gt;
&lt;p&gt;Nx Cloud pairs with Nx in order to enable you to build and test code more rapidly, by up to 10 times. Even teams that are new to Nx can connect to Nx Cloud and start saving time instantly. Visit &lt;a href=&quot;https://nx.app/&quot;&gt;Nx Cloud&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/nx-cloud.png&quot; alt=&quot;Nx cloud&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;features-and-roadmap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#features-and-roadmap&quot; aria-label=&quot;features and roadmap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Features and Roadmap&lt;/h2&gt;
&lt;p&gt;I set the tentative deadline for motivating myself to finish the work on time. Otherwise, It will take forever to complete :)&lt;/p&gt;
&lt;h3 id=&quot;10---simple-spotify-client&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#10---simple-spotify-client&quot; aria-label=&quot;10   simple spotify client permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.0 - Simple Spotify client&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;March 01 - 28, 2021&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Proven, scalable, and easy to understand structure with Nx workspace&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Play music using Spotify SDK&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Load a maximum of 50 save playlists and top 100 songs per playlist.&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; Cool visualization&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;live-stream&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#live-stream&quot; aria-label=&quot;live stream permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Live stream&lt;/h2&gt;
&lt;p&gt;Let work on it together!&lt;/p&gt;
&lt;p&gt;I scheduled a few live stream sessions to show you how I continue developing Angular Spotify. Follow &lt;a href=&quot;https://twitter.com/trungvose&quot;&gt;my twitter&lt;/a&gt; for the latest updates. See the scheduled events.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Description/Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Sat, 3rd April 2021, 10AM&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=9njo6MZWBN0&quot;&gt;Structure your Angular application with Nx workspace&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Sat, 10th April 2021, 10AM&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=vEIxjcrXcDc&quot;&gt;Build the album list page&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Sat, 17th April 2021, 10AM&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://youtu.be/c9-WTksAv-s&quot;&gt;Build the album detail page&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Sat, 24th April 2021, 10AM&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=8P3pB40JF2w&quot;&gt;Build the artist detail page&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Sat, 1st May 2021, 10AM&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://youtu.be/Oj4yomnxfj4&quot;&gt;Build the track list page&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;TBD&lt;/td&gt;
&lt;td&gt;Setup i18n and support new language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;TBD&lt;/td&gt;
&lt;td&gt;Config Nx build:affected with Github action&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I will also do some refactoring with &lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt; for Angular Vietnam Office Hours. More detail is coming soon.&lt;/p&gt;
&lt;h2 id=&quot;time-spending&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#time-spending&quot; aria-label=&quot;time spending permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Time spending&lt;/h2&gt;
&lt;p&gt;It is a side project that I only spent time outside of the office hours to work on. I initially planned to complete the project within two weeks, but the first two weekends were not very productive, maybe because of the holiday mood from Lunar New Year :) But once the lego blocks are getting together, I feel the rhythm, and I know it has to be finished by the end of March.&lt;/p&gt;
&lt;p&gt;I couldn’t get the full-time report from waka time because it only shows me the latest two weeks. 🤣&lt;/p&gt;
&lt;p&gt;I have spent approximately 50 hours working on this project, which is almost the same amount that I worked on the first version of &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The visualizer was the most exciting feature, and I decided to start this project because of that single component.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-spotify/raw/main/libs/web/shared/assets/src/assets/readme/time-spending.png&quot; alt=&quot;Angular Spotify - Time spending&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;accessibility-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accessibility-&quot; aria-label=&quot;accessibility  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Accessibility ♿&lt;/h3&gt;
&lt;p&gt;Not all components have properly defined &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA&quot;&gt;aria attributes&lt;/a&gt;, visual focus indicators, etc.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-development-environment-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-up-the-development-environment-&quot; aria-label=&quot;setting up the development environment  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting up the development environment 🛠&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;git clone https://github.com/trungvose/angular-spotify.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cd angular-spotify&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; for starting Angular web application&lt;/li&gt;
&lt;li&gt;The app should run on &lt;code class=&quot;language-text&quot;&gt;http://localhost:4200/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;unitintegration-tests-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#unitintegration-tests-&quot; aria-label=&quot;unitintegration tests  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Unit/Integration tests 🧪&lt;/h3&gt;
&lt;p&gt;I skipped writing test for this project.&lt;/p&gt;
&lt;h2 id=&quot;compatibility&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compatibility&quot; aria-label=&quot;compatibility permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compatibility&lt;/h2&gt;
&lt;p&gt;Web Playback SDK provided supports for Chrome, Firefox, Edge, IE 11, or above running on Mac/Windows/Linux.&lt;/p&gt;
&lt;p&gt;It &lt;strong&gt;doesn’t support&lt;/strong&gt; Safari or any mobile browser on &lt;strong&gt;Android&lt;/strong&gt; or &lt;strong&gt;iOS&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;View &lt;a href=&quot;https://developer.spotify.com/documentation/web-playback-sdk/#supported-browsers&quot;&gt;completed list of supported browsers&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;author-trung-vo-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#author-trung-vo-%EF%B8%8F&quot; aria-label=&quot;author trung vo ️ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Author: Trung Vo ✍️&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A seasoned front-end engineer with seven years of passion in creating experience-driven products. I am proficient in Angular, React and TypeScript development.&lt;/li&gt;
&lt;li&gt;Personal blog: &lt;a href=&quot;https://trungvose.com/&quot;&gt;https://trungvose.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Say hello: trungk18 [et] gmail [dot] com&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;contributing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contributing&quot; aria-label=&quot;contributing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contributing&lt;/h2&gt;
&lt;p&gt;If you have any ideas, just &lt;a href=&quot;https://github.com/trungvose/angular-spotify/issues/new&quot;&gt;open an issue&lt;/a&gt; and tell me what you think.&lt;/p&gt;
&lt;p&gt;If you’d like to contribute, please fork the repository and make changes as you’d like. &lt;a href=&quot;https://github.com/trungvose/angular-spotify/compare&quot;&gt;Pull requests&lt;/a&gt; are warmly welcome.&lt;/p&gt;
&lt;h2 id=&quot;credits-and-reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#credits-and-reference&quot; aria-label=&quot;credits and reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Credits and reference&lt;/h2&gt;
&lt;p&gt;Special thanks to my friend &lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt;, who helped me review the code early.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github1s.com/koel/core/blob/master/js/utils/visualizer.ts&quot;&gt;@koel/koel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A cool player made by &lt;a href=&quot;https://twitter.com/notphanan&quot;&gt;@phanan&lt;/a&gt;, I reused the visualizer code from this repo with my additional customization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/beeman/component-store-playground&quot;&gt;beeman/component-store-playground&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A nice example of using Nx with ngrx/component-store, I refer to the project structure from this repo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://timdeschryver.dev/blog/start-using-ngrx-effects-for-this&quot;&gt;Start using ngrx/effects for this&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An excellent write up by &lt;a href=&quot;https://twitter.com/tim_deschryver&quot;&gt;Tim Deschryver&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;license&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#license&quot; aria-label=&quot;license permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;License&lt;/h2&gt;
&lt;p&gt;Feel free to use my code on your project. Please put a reference to this repository.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://opensource.org/licenses/MIT&quot;&gt;MIT&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Nx Workspace structure for an application with NestJS and Angular]]></title><description><![CDATA[This structure has been serving us really really well by enforcing consistency, promoting SCAM, prevent circular dependencies, and minimize mental overhead of "where to put what"]]></description><link>https://trungvose.comnx-workspace-structure-angular-nestjs/</link><guid isPermaLink="false">https://trungvose.comnx-workspace-structure-angular-nestjs/</guid><pubDate>Wed, 24 Mar 2021 10:15:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;original-gist&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#original-gist&quot; aria-label=&quot;original gist permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Original Gist&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/trungk18/7ef8766cafc05bc8fd87be22de6c5b12&quot;&gt;https://gist.github.com/trungk18/7ef8766cafc05bc8fd87be22de6c5b12&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;principles&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#principles&quot; aria-label=&quot;principles permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Principles&lt;/h2&gt;
&lt;p&gt;Below is the sample folder structure for Nx with NestJS and Angular. Our principles are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SCAMs (single component Angular modules) for tree-shakable components, meaning each component will have a respective module. For example, a &lt;code class=&quot;language-text&quot;&gt;RegisterComponent&lt;/code&gt; will have a corresponding &lt;code class=&quot;language-text&quot;&gt;RegisterModule&lt;/code&gt;, we won’t declare &lt;code class=&quot;language-text&quot;&gt;RegisterComponent&lt;/code&gt; as part of &lt;code class=&quot;language-text&quot;&gt;AuthModule&lt;/code&gt; for example.&lt;/li&gt;
&lt;li&gt;Mostly everything will stay in the &lt;code class=&quot;language-text&quot;&gt;libs&lt;/code&gt; folder. New modules, new models, new configurations, new components etc… are in libs. libs should be separated into different directories based on existing apps. We won’t put them inside the &lt;code class=&quot;language-text&quot;&gt;apps&lt;/code&gt; folder. For example in an Angular, it contains the &lt;code class=&quot;language-text&quot;&gt;main.ts&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;app.component.ts&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;app.module.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;structure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#structure&quot; aria-label=&quot;structure permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Structure&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.
└── root
    ├── apps
    │   ├── api (nestjs)
    │   └── client (angular)
    └── libs (1)
        ├── api (dir)
        │   ├── core (dir)
        │   │   └── feature (nest:lib) (2)
        │   ├── feature-1 (dir)
        │   │   ├── data-access (nest:lib, service + entities)
        │   │   ├── feature (nest:lib, module + controller)
        │   │   └── utils (nest:lib, things like interceptors, guards, pipes etc...)
        │   └── feature-2 (dir)
        │       ├── data-access (nest:lib, service + entities)
        │       ├── feature (nest:lib, module + controller)
        │       └── utils (nest:lib, things like interceptors, guards, pipes etc...)
        ├── client (dir)
        │   ├── shell (dir)
        │   │   └── feature (angular:lib) (3)
        │   └── feature-1 (dir)
        │       ├── data-access (angular:lib, service, API calls, state management)
        │       ├── feature (4)
        │       │   ├── list (angular:lib e.g. ProductList)
        │       │   └── detail (angular:lib e.g. ProductDetail)
        │       ├── ui (dir)
        │       │   ├── comp-1 (angular:lib, SCAM for Component)
        │       │   └── pipe-1 (angular:lib, SCAM for Pipe)
        │       └── shared (dir)
        │           ├── data-access (angular:lib, any Service or State management to share across the Client app)
        │           ├── ui (dir, 5)
        │           └── utils (angular:lib, usually shared Guards, Interceptors, Validators...)
        └── shared (dir, most libs in here are buildable @nrwl/angular:lib)
            ├── data-access (my shared data-access is usually models, so it is a lib)
            ├── ui (optional dir, if I have multiple client apps)
            └── utils (optional dir, usually validation logic or shared utilities)
                ├── util1 (lib)
                └── util2 (lib)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(1) lib vs dir&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a dir is just a directory.&lt;/li&gt;
&lt;li&gt;a lib is generated by using Nx schematics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(2) &lt;code class=&quot;language-text&quot;&gt;api-core-feature&lt;/code&gt;: this is the CoreModule that will include all initial setups like Config and Database Connection etc… and importing other Modules. CoreModule will be imported by AppModule&lt;/p&gt;
&lt;p&gt;(3) &lt;code class=&quot;language-text&quot;&gt;client-shell-feature&lt;/code&gt;: Same idea as NestJS’s CoreModule. This Shell includes &lt;code class=&quot;language-text&quot;&gt;RouterModule.forRoot()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(4) &lt;code class=&quot;language-text&quot;&gt;client-feature-1-feature&lt;/code&gt;: This can either a dir or a lib.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If this feature only has &lt;strong&gt;one&lt;/strong&gt; Routable component, it is a lib.&lt;/li&gt;
&lt;li&gt;If it has &lt;strong&gt;multiple&lt;/strong&gt; Routable components, then it should be a dir. For example:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;└── feature
    ├── list (angular:lib e.g ProductList)
    └── detail (angular:lib e.g. ProductDetail)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt; usually contains the ContainerComponent and the RouterModule.forChild()&lt;/p&gt;
&lt;p&gt;(5) &lt;code class=&quot;language-text&quot;&gt;client-shared-ui&lt;/code&gt; is a little tricky. The general recommendation is to &lt;strong&gt;NOT&lt;/strong&gt; grouped stuffs by type like &lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;pipes&lt;/code&gt; etc… into a single module but because these are shared, it is easy to get quite messy if not grouped by type. This is your call. We prefer to have a Single Component Per Module (SCAM) for each angular library.&lt;/p&gt;
&lt;p&gt;This structure is proposed by my friend &lt;a href=&quot;https://github.com/nartc&quot;&gt;Chau Tran&lt;/a&gt; and I am applying it for my latest project!&lt;/p&gt;
&lt;h2 id=&quot;why&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why&quot; aria-label=&quot;why permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why?&lt;/h2&gt;
&lt;p&gt;Following the above structure will bring three advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consistency: eliminate mental overhead when we don’t have to think about where to put what in a big repo having from two apps and above.&lt;/li&gt;
&lt;li&gt;Promote Single Component Per Module (SCAM) + Buildable libraries to get the benefits from the nx affected commands.&lt;/li&gt;
&lt;li&gt;Prevent circular dependencies issue.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;some-rules-of-thumb&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-rules-of-thumb&quot; aria-label=&quot;some rules of thumb permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some rules of thumb&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;data-access&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;data-access&lt;/code&gt; can import other &lt;code class=&quot;language-text&quot;&gt;data-access&lt;/code&gt;. But never import its &lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt; . For example: &lt;code class=&quot;language-text&quot;&gt;user/data-access&lt;/code&gt; can import from &lt;code class=&quot;language-text&quot;&gt;product/data-access&lt;/code&gt; but it will never import from &lt;code class=&quot;language-text&quot;&gt;user/feature&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt;: can only import its own &lt;code class=&quot;language-text&quot;&gt;data-access&lt;/code&gt; or the global &lt;code class=&quot;language-text&quot;&gt;shared/data-access&lt;/code&gt;. For example: &lt;code class=&quot;language-text&quot;&gt;user/feature&lt;/code&gt; can import from &lt;code class=&quot;language-text&quot;&gt;user/data-access&lt;/code&gt; but never from &lt;code class=&quot;language-text&quot;&gt;product/data-access&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;util&lt;/code&gt;: Utils can be shared across &lt;code class=&quot;language-text&quot;&gt;data-access&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;util&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[SVG fill color doesn't work with hex colors]]></title><description><![CDATA[Because # in URLs starts a fragment identifier. So, in order to make that work, write %23 instead of #, that is the value of escaped # character.]]></description><link>https://trungvose.comsvg-fill-with-hex-color/</link><guid isPermaLink="false">https://trungvose.comsvg-fill-with-hex-color/</guid><pubDate>Wed, 17 Mar 2021 10:15:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Basically, this works fine with &lt;code class=&quot;language-text&quot;&gt;fill=&apos;red&apos;&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;data:image/svg+xml;utf8,&amp;lt;svg xmlns=&apos;http://www.w3.org/2000/svg&apos; width=&apos;20&apos; height=&apos;10&apos; &gt;&amp;lt;path fill=&apos;red&apos; d=&apos;M 0,10 H 20 L 10,0 Z&apos; /&gt;&amp;lt;/svg&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This doesn’t work as well with &lt;code class=&quot;language-text&quot;&gt;fill=&apos;#FF0000&apos;&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;data:image/svg+xml;utf8,&amp;lt;svg xmlns=&apos;http://www.w3.org/2000/svg&apos; width=&apos;20&apos; height=&apos;10&apos; &gt;&amp;lt;path fill=&apos;#FF0000&apos; d=&apos;M 0,10 H 20 L 10,0 Z&apos; /&gt;&amp;lt;/svg&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;reason-and-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reason-and-solution&quot; aria-label=&quot;reason and solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reason and solution&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;#&lt;/code&gt; in URLs starts a fragment identifier. So, in order to make that work, write &lt;code class=&quot;language-text&quot;&gt;%23&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;#&lt;/code&gt;. That is the value of escaped &lt;code class=&quot;language-text&quot;&gt;#&lt;/code&gt; character. It works with &lt;code class=&quot;language-text&quot;&gt;fill=&apos;%23FF0000&apos;&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;data:image/svg+xml;utf8,&amp;lt;svg xmlns=&apos;http://www.w3.org/2000/svg&apos; width=&apos;20&apos; height=&apos;10&apos; &gt;&amp;lt;path fill=&apos;%23FF0000&apos; d=&apos;M 0,10 H 20 L 10,0 Z&apos; /&gt;&amp;lt;/svg&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wondering what is &lt;code class=&quot;language-text&quot;&gt;Fragment Identifiers&lt;/code&gt;, check this article&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/svg-fragment-identifiers-work/&quot;&gt;https://css-tricks.com/svg-fragment-identifiers-work/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/a/61099329/3375906&quot;&gt;https://stackoverflow.com/a/61099329/3375906&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#demo&quot; aria-label=&quot;demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Demo&lt;/h2&gt;
&lt;iframe height=&quot;350&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Hexadecimal color values in SVG as data url&quot; src=&quot;https://codepen.io/gunnarbittersmann/embed/BoovjR?height=350&amp;theme-id=dark&amp;default-tab=html,result&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&apos;https://codepen.io/gunnarbittersmann/pen/BoovjR&apos;&gt;Hexadecimal color values in SVG as data url&lt;/a&gt; by Gunnar Bittersmann
  (&lt;a href=&apos;https://codepen.io/gunnarbittersmann&apos;&gt;@gunnarbittersmann&lt;/a&gt;) on &lt;a href=&apos;https://codepen.io&apos;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[TypeScript unknown vs any types]]></title><description><![CDATA[The main difference between unknown and any is that unknown is much less permissive than any: we have to do some form of checking before performing most operations on values of type unknown, whereas we don't have to do any checks before performing operations on values of type any.]]></description><link>https://trungvose.comunknown-vs-any/</link><guid isPermaLink="false">https://trungvose.comunknown-vs-any/</guid><pubDate>Sat, 13 Mar 2021 10:15:00 GMT</pubDate><content:encoded>&lt;p&gt;TypeScript 3.0 introduced a new &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; type which is the type-safe counterpart of the &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;The main difference between &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; is that &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; is much less permissive than &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;: we have to do some form of checking before performing most operations on values of type &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt;, whereas we don’t have to do any checks before performing operations on values of type &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mariusschulz.com/blog/the-unknown-type-in-typescript&quot;&gt;The unknown Type in TypeScript - Marius Schulz&lt;/a&gt;
This is a great article and I recommended you read the full one. I only quote some of the important parts :)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/51439876/3375906&quot;&gt;https://stackoverflow.com/a/51439876/3375906&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-any-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-any-type&quot; aria-label=&quot;the any type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The any Type&lt;/h2&gt;
&lt;p&gt;Let’s first look at the &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; type so that we can better understand the motivation behind introducing the unknown type.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; type has been in TypeScript since the first release in 2012. It represents all possible JavaScript values — primitives, objects, arrays, functions, errors, symbols, what have you.&lt;/p&gt;
&lt;p&gt;In TypeScript, every type is assignable to &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;. This makes &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; a &lt;a href=&quot;https://en.wikipedia.org/wiki/Top_type&quot;&gt;top type&lt;/a&gt; (also known as a universal supertype) of the type system.&lt;/p&gt;
&lt;p&gt;Here are a few examples of values that we can assign to a variable of type &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;

value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello World&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;random &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Symbol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; type is essentially an escape hatch from the type system. As developers, this gives us a ton of freedom: TypeScript lets us perform any operation we want on values of type any without having to perform any kind of checking beforehand.&lt;/p&gt;
&lt;p&gt;In the above example, the value variable is typed as &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;. Because of that, TypeScript considers all of the following operations to be type-correct:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;

value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bar &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In many cases, this is too permissive. Using the any type, it’s easy to write code that is type-correct, but problematic at runtime. We don’t get a lot of protection from TypeScript if we’re opting to use any.&lt;/p&gt;
&lt;p&gt;What if there were a top type that was safe by default? This is where unknown comes into play.&lt;/p&gt;
&lt;h2 id=&quot;the-unknown-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-unknown-type&quot; aria-label=&quot;the unknown type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The unknown Type&lt;/h2&gt;
&lt;p&gt;Just like all types are assignable to &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;, all types are assignable to &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt;. This makes &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; another top type of TypeScript’s type system (the other one being &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Here’s the same list of assignment examples we saw before, this time using a variable typed as &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;

value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello World&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;random &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TypeError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Symbol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All assignments to the value variable are considered type-correct.&lt;/p&gt;
&lt;p&gt;What happens though when we try to assign a value of type &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; to variables of other types?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value1&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value2&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value3&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value4&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value5&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value6&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; object &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value7&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value8&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; type is only assignable to the &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; type and the &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; type itself. Intuitively, this makes sense: only a container that is capable of holding values of arbitrary types can hold a value of type &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt;; after all, we don’t know anything about what kind of value is stored in value.&lt;/p&gt;
&lt;p&gt;Let’s now see what happens when we try to perform operations on values of type &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt;. Here are the same operations we’ve looked at before:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;

value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bar &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;
value&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;narrowing-the-unknown-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#narrowing-the-unknown-type&quot; aria-label=&quot;narrowing the unknown type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Narrowing the unknown Type&lt;/h2&gt;
&lt;p&gt;We can narrow the unknown type to a more specific type in different ways, including the &lt;code class=&quot;language-text&quot;&gt;typeof&lt;/code&gt; operator, the &lt;code class=&quot;language-text&quot;&gt;instanceof&lt;/code&gt; operator, and custom type guard functions.&lt;/p&gt;
&lt;p&gt;The following example illustrates how value has a more specific type within the two &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; statement branches:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stringifyForLogging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;function&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Within this branch, `value` has type `Function`,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// so we can access the function&apos;s `name` property&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; functionName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;(anonymous)&apos;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[function &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;functionName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Within this branch, `value` has type `Date`,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// so we can call the `toISOString` method&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toISOString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In addition to using the &lt;code class=&quot;language-text&quot;&gt;typeof&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;instanceof&lt;/code&gt; operators, we can also narrow the unknown type using a custom type guard function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * A custom type guard function that determines whether
 * `value` is an array that only contains numbers.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isNumberArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;every&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; element &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;number&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; unknownValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNumberArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;unknownValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Within this branch, `unknownValue` has type `number[]`,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// so we can spread the numbers as arguments to `Math.max`&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; max &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;unknownValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice how &lt;code class=&quot;language-text&quot;&gt;unknownValue&lt;/code&gt; has type &lt;code class=&quot;language-text&quot;&gt;number[]&lt;/code&gt; within the if statement branch although it is declared to be of type &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;using-type-assertions-with-unknown&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-type-assertions-with-unknown&quot; aria-label=&quot;using type assertions with unknown permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using Type Assertions with unknown&lt;/h2&gt;
&lt;p&gt;If you want to force the compiler to trust you that a value of type &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; is of a given type, you can use a type assertion like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello World&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; someString&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; otherString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; someString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;HELLO WORLD&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Be aware that TypeScript is not performing any special checks to make sure the type assertion is actually valid. The type checker assumes that you know better and trusts that whatever type you’re using in your type assertion is correct.&lt;/p&gt;
&lt;p&gt;This can easily lead to an error being thrown at runtime if you make a mistake and specify an incorrect type:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; someString&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; otherString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; someString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// BOOM&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The value variable holds a number, but we’re pretending it’s a string using the type assertion &lt;code class=&quot;language-text&quot;&gt;value as string&lt;/code&gt;. Be careful with type assertions!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Convert Promise to Observable]]></title><description><![CDATA[You'll need those tips one day if you get used to RxJS 🤣]]></description><link>https://trungvose.comconvert-promise-to-observable/</link><guid isPermaLink="false">https://trungvose.comconvert-promise-to-observable/</guid><pubDate>Sun, 07 Mar 2021 10:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Promise is the answer to the callback hell problem. With the introduction of &lt;code class=&quot;language-text&quot;&gt;async/await&lt;/code&gt; syntax, Promise is getting so popular. But if you work with Angular, you’ll probably need an Observable from Promise sometimes. There are few ways for you to do so.&lt;/p&gt;
&lt;p&gt;Assume that I have a function, &lt;code class=&quot;language-text&quot;&gt;getPromise&lt;/code&gt;, that resolves after two seconds and does a simple console.log.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getPromise&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;source&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Promise created for&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Promise Resolved: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;source&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;1-direct-execution--conversion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-direct-execution--conversion&quot; aria-label=&quot;1 direct execution  conversion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Direct Execution / Conversion&lt;/h2&gt;
&lt;p&gt;Use &lt;a href=&quot;https://rxjs-dev.firebaseapp.com/api/index/function/from&quot;&gt;from&lt;/a&gt; to directly convert a previously created Promise to an Observable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; from &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// getPromise() is called once, the promise is passed to the Observable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observableFrom$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;FROM&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
observableFrom$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
observableFrom$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;observableFrom$&lt;/code&gt; will be a &lt;strong&gt;hot Observable&lt;/strong&gt; that effectively replays the Promises value to Subscribers.&lt;/p&gt;
&lt;p&gt;It’s a hot Observable because the producer (in this case, the Promise) is created outside of the Observable. And because Promise is &lt;code class=&quot;language-text&quot;&gt;eager&lt;/code&gt;, you’ll start seeing &lt;code class=&quot;language-text&quot;&gt;Promise created for FROM&lt;/code&gt; on the browser when running &lt;code class=&quot;language-text&quot;&gt;from(getPromise())&lt;/code&gt; console immediately after you call &lt;code class=&quot;language-text&quot;&gt;from(getPromise(&quot;FROM&quot;))&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Promise created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FROM&lt;/span&gt;
Promise Resolved&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FROM&lt;/span&gt;
Promise Resolved&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FROM&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Multiple subscribers will share the same Promises, which means if you subscribe to &lt;code class=&quot;language-text&quot;&gt;observable$&lt;/code&gt; multiple times, you’ll still see only one &lt;code class=&quot;language-text&quot;&gt;Promise created for FROM&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;2-deferred-execution-on-every-subscribe&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-deferred-execution-on-every-subscribe&quot; aria-label=&quot;2 deferred execution on every subscribe permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Deferred Execution On Every Subscribe&lt;/h2&gt;
&lt;p&gt;Use &lt;a href=&quot;https://rxjs-dev.firebaseapp.com/api/index/function/defer&quot;&gt;defer&lt;/a&gt; with a Promise factory function as input to defer the creation and conversion of a Promise to an Observable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; defer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// getPromise() is called every time someone subscribes to the observable$&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observableDefer$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;DEFER&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
observableDefer$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
observableDefer$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;observableDefer$&lt;/code&gt; here will be a &lt;strong&gt;cold Observable&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It’s a cold Observable because the producer (the Promise) is created inside of the Observable. Each subscriber will create a new Promise by calling the given Promise factory function.&lt;/p&gt;
&lt;p&gt;This allows you to create an &lt;code class=&quot;language-text&quot;&gt;observableDefer$&lt;/code&gt; without creating and thus executing a Promise right away and without sharing this Promise with multiple subscribers. Each subscriber to &lt;code class=&quot;language-text&quot;&gt;observableDefer$&lt;/code&gt; effectively calls &lt;code class=&quot;language-text&quot;&gt;from(promiseFactory()).subscribe(subscriber)&lt;/code&gt;. Meaning if you subscribe to &lt;code class=&quot;language-text&quot;&gt;observableDefer$&lt;/code&gt; multiple times, you’ll see &lt;code class=&quot;language-text&quot;&gt;Promise created for FROM&lt;/code&gt; each time.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Promise created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFER&lt;/span&gt;
Promise created &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFER&lt;/span&gt;
Promise Resolved&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFER&lt;/span&gt;
Promise Resolved&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFER&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;3-many-operators-accept-promises-directly&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-many-operators-accept-promises-directly&quot; aria-label=&quot;3 many operators accept promises directly permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Many Operators Accept Promises Directly&lt;/h2&gt;
&lt;p&gt;Most RxJS operators that combine (e.g. &lt;code class=&quot;language-text&quot;&gt;merge&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;concat&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;forkJoin&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;combineLatest&lt;/code&gt; …) or transform observables (e.g. &lt;code class=&quot;language-text&quot;&gt;switchMap&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;mergeMap&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;concatMap&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;catchError&lt;/code&gt; …) accept promises directly. If you’re using one of them anyway, you don’t have to use &lt;code class=&quot;language-text&quot;&gt;from&lt;/code&gt; to wrap a promise first (but to create a &lt;strong&gt;cold observable&lt;/strong&gt; you still might have to use &lt;code class=&quot;language-text&quot;&gt;defer&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Execute two promises simultaneously&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;forkJoin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;switchMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;v1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v2&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; v1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// map to nested Promise&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Check the &lt;a href=&quot;https://rxjs-dev.firebaseapp.com/api&quot;&gt;documentation&lt;/a&gt; or &lt;a href=&quot;https://github.com/ReactiveX/rxjs/tree/master/src/internal&quot;&gt;implementation&lt;/a&gt; to see if the operator you’re using accepts &lt;code class=&quot;language-text&quot;&gt;ObservableInput&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;SubscribableOrPromise&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObservableInput&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; SubscribableOrPromise&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; ArrayLike&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Iterable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Note the PromiseLike ----------------------------------------------------v&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SubscribableOrPromise&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Subscribable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Subscribable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; PromiseLike&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; InteropObservable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/convert-promise-to-observable?file=index.ts&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/a/54111045/3375906&quot;&gt;Convert Promise to Observable - answer by frido&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Migrate Angular to ESLint]]></title><description><![CDATA[I'll show you how to migrate from TSLint to ESLint and using husky to run lint every time you try to make a commit]]></description><link>https://trungvose.commigrate-angular-eslint/</link><guid isPermaLink="false">https://trungvose.commigrate-angular-eslint/</guid><pubDate>Sat, 23 Jan 2021 04:18:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;tslint-is-deprecated&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tslint-is-deprecated&quot; aria-label=&quot;tslint is deprecated permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TSLint is deprecated&lt;/h2&gt;
&lt;p&gt;TSLint has been a default configuration for linting with Angular CLI for a long time, until its author decided to &lt;a href=&quot;https://github.com/palantir/tslint/issues/3865#issuecomment-519302911&quot;&gt;deprecated TSLint&lt;/a&gt;. If you run &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt; in your project that was created with CLI, you will get the below warning.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 756px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/817d877516ac454ccc3b6995045d00f2/3c4de/01-tslintOutdated.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAc2CAr//xAAWEAADAAAAAAAAAAAAAAAAAAAAEBH/2gAIAQEAAQUCUP/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABYQAAMAAAAAAAAAAAAAAAAAAAAQMf/aAAgBAQAGPwIi/8QAGxAAAQQDAAAAAAAAAAAAAAAAEQABMZEhUaH/2gAIAQEAAT8hGI6gYZaLTWv/2gAMAwEAAgADAAAAEHvP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Qh//EABoQAAMAAwEAAAAAAAAAAAAAAAABESExUYH/2gAIAQEAAT8QVo4yuhQvqLvqH//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/817d877516ac454ccc3b6995045d00f2/3c4de/01-tslintOutdated.jpg&quot;
        srcset=&quot;/static/817d877516ac454ccc3b6995045d00f2/09b79/01-tslintOutdated.jpg 240w,
/static/817d877516ac454ccc3b6995045d00f2/7cc5e/01-tslintOutdated.jpg 480w,
/static/817d877516ac454ccc3b6995045d00f2/3c4de/01-tslintOutdated.jpg 756w&quot;
        sizes=&quot;(max-width: 756px) 100vw, 756px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As suggested in the warning message, head over to &lt;a href=&quot;https://github.com/angular-eslint/angular-eslint&quot;&gt;angular-eslint&lt;/a&gt;, and we will prepare to migrate away from TSLint.&lt;/p&gt;
&lt;h2 id=&quot;migrate-to-angular-eslint&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#migrate-to-angular-eslint&quot; aria-label=&quot;migrate to angular eslint permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Migrate to angular-eslint&lt;/h2&gt;
&lt;p&gt;I will use this opportunity to migrate &lt;a href=&quot;https://github.com/trungvose/angular-tetris&quot;&gt;angular-tetris&lt;/a&gt; to Angular 11 and angular-eslint&lt;/p&gt;
&lt;h3 id=&quot;prerequisite&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prerequisite&quot; aria-label=&quot;prerequisite permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Prerequisite&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/angular-eslint/angular-eslint&quot;&gt;angular-eslint&lt;/a&gt; support Angular CLI 10.1.0 and onwards, including Angular CLI 11.x.&lt;/p&gt;
&lt;p&gt;So that, make sure that you update your Angular CLI first before going ahead. Suppose you install &lt;code class=&quot;language-text&quot;&gt;@angular/cli&lt;/code&gt; globally. Please run the following command to update its version.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i @angular/cli&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you have @angular/cli as the devDependencies, you can run.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng update @angular/cli&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To perform update both the CLI and all of @angular related packages, you can run.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng update @angular/cli @angular/core&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;step-1-add-relevant-dependencies&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-add-relevant-dependencies&quot; aria-label=&quot;step 1 add relevant dependencies permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: Add relevant dependencies&lt;/h3&gt;
&lt;p&gt;The first step is to run the schematic to add @angular-eslint to your project:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; @angular-eslint/schematics&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This command adds the needed dependencies for &lt;code class=&quot;language-text&quot;&gt;angular-eslint&lt;/code&gt;, which looks like the below screenshot.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 600px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f32d1094b624e1809079800ba1b3867b/0a47e/02-eslint.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 83.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADRElEQVQ4y42SWXPiRhRG9RvipGKzaRe72AXIEjAsg22EsRDgmHiZcZyK8TqZSVV+/klJOBN7ah7ycOrrbtU9dW+3hJws02636fV6UbZaLbrdLo7rYts2pmmiaTq6bvwvhGwySdU0sZpNarUahUIBVVVQFAVFlpEkEVEUSYWkwrWEKEnbfEF6tRckUSRtGORyOQzDQFZV0qUaWt5EL1YwSjVkRaGgK+R1BTGVIh6PE08kokwkEm8QVFVF1/VIFsoVTafWP6Ts9qmPPCqdEabVptZs03C6FBrtaLS8rlIvZNEUhWQySSqVihCUUGgYGJkMkiIjGWkKdofyuzF5yyaXMaLCWjGHpCjEUykS4pZkmKm3CIaskNUMspqOkRLJyCqVuo3lDimls9hpjW4hS16SkXdjaLHEK+Lf7BMIYyXFoS4zkpKcxPa4+mmX3yyX67LFx0qLG1njXhbZSCI3sV3Of9jhcudHLnZ2vovQdA0sR6M3qvJuXKQ/NZl+nDK9OuJo5TDxTFazEsNBFrefw3J0mo5By/0+QqOTptExcAblSOZdNZg/TJk/ePibMYvNmNNHD+/mgPG8jL8o46+qOO/zVGydbf1/CFa4cA3cQYW+ZzI5r+PfTfHvJgRPAfOnOf7DjOBxRvChxflNl18+NBl5RXrjAmG91TG2GXb4r7AzrDKYlphc1Leihxnz+ynB4wnzOy/KUDy77rL4tcLZRZ3BJE9tX6fuGBGN18Jth0W8ywarv85ZfFpycnuAf+d9FfubQ4I7j+X9BP+qzcm6xey0iT3I0BnlaPW+HXlSZBIKv6xZPC8iYSjzXzqcPxwzf/Txb/qc3o5Z3R4wXDQ4OC4yDSq47/MIDfdFONx2GI68/LwmeF4w+2Mc3efJZsLiz1OCpzknt0f4vw+ZX7ZYrqscBxX2Bzmq4QOFIze74evodEZVhjOT42uLs7/POf2yYPlpxuL5mOXzjNXnIMrw7Cz8thkQrKusLy0O55XoT4lGLtUMyo009VaRhp1jv2/iTDs4nsP+YTPCPrBwJvs4RzbtYQmnm2YwKFBu6GRMmXxZ/YogSwqqqqFpaXRVQown2fs5Rmw3/g0vZ3shCfb2EsRjSRLxt/wD+7MulvcFBFIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/f32d1094b624e1809079800ba1b3867b/0a47e/02-eslint.png&quot;
        srcset=&quot;/static/f32d1094b624e1809079800ba1b3867b/8ff5a/02-eslint.png 240w,
/static/f32d1094b624e1809079800ba1b3867b/e85cb/02-eslint.png 480w,
/static/f32d1094b624e1809079800ba1b3867b/0a47e/02-eslint.png 600w&quot;
        sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-2-run-the-convert-tslint-to-eslint-schematic-on-a-project&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-run-the-convert-tslint-to-eslint-schematic-on-a-project&quot; aria-label=&quot;step 2 run the convert tslint to eslint schematic on a project permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: Run the convert-tslint-to-eslint schematic on a project&lt;/h3&gt;
&lt;p&gt;The next thing to do is consider which “project” you want to migrate to use ESLint. If you have a single application in your workspace, you will likely have just a single entry in the projects configuration object within your angular.json file. If you have a projects/ directory in your workspace, you will have multiple entries in your projects configuration, and you will need to chose which one you want to migrate using the &lt;code class=&quot;language-text&quot;&gt;convert-tslint-to-eslint&lt;/code&gt; schematic.&lt;/p&gt;
&lt;p&gt;In my use case, I only have a single application - &lt;code class=&quot;language-text&quot;&gt;angular-tetris&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 650px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5ab351e891e348ddcd34b29f76078e40/a6d36/03-angularJson.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABw0lEQVQoz3WS23LTMBRF/R3gi+6Sb7HsODhtCm0hlMD//89ipGQ6hZk8rPGWbO1ztnyKV9OyHY9s28Y0TUip0NqglMYY+66VMll/3EvaWvcPxcV0PG4bj6cTcZ6x1mbjZV7w3jOOI8uyEGOk73tCaJmmyLquhBBomgYhJEKITGHrBikEfR8IwdO2ga7rGIYR5z1CykwjBHXTUNfN9bCUVHXN57J8pyxLijq9bBqsk7gx0sYVpSRBCXbBMjhDZxTTTbdGZXqr83r0lik4emtoraaQUqJSF9qy//GH5fXCuB7ox5HemWyUjJN2WqGlwEiBVTLj9A0lMUpeDVNkZSzTtzPD6ZXh+BW/WxFK5qqd1Tnmp7KkqirKj5QfqKpbh0pR1zXLduLp+4V1f+Cw35h3EW9MvpLUmRRNNkzf3qNIlbNhVeUox+czj+ffbC8/Ocwda3uNnO7JakVVXQ/eMy6SmdY6L7QRTMeN9fyL6fREPO4Zvzxghwk/H+gOD4QxooW6b2hMGlaTKzsjMUYTn9+IL2/Eec+y7FGhz2MjUpLmGvseRRpe71029E7Tu4A0Dt8PGB9Q1mF8i9bq+vNuiVIT/z8TfwH5oD1eaoDtWgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/5ab351e891e348ddcd34b29f76078e40/a6d36/03-angularJson.png&quot;
        srcset=&quot;/static/5ab351e891e348ddcd34b29f76078e40/8ff5a/03-angularJson.png 240w,
/static/5ab351e891e348ddcd34b29f76078e40/e85cb/03-angularJson.png 480w,
/static/5ab351e891e348ddcd34b29f76078e40/a6d36/03-angularJson.png 650w&quot;
        sizes=&quot;(max-width: 650px) 100vw, 650px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I will run the following command to migrate to ESLint.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ng g @angular-eslint/schematics:convert-tslint-to-eslint angular-tetris&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It will do two things.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt; to replace &lt;code class=&quot;language-text&quot;&gt;tslint&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;angular-eslint&lt;/code&gt; for linting configuration.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 620px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4a13a0f8e010f30ad240546ffe7bf45b/2a195/04-angularJSONEslint.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACaUlEQVQoz43La0/TYADF8b4yopF1a1d2aZ92XS/r2m5lY1fHBMRLgLExREzAiKCA14hRExP0K+g3/huJiYkR44tfTnJOjrTgeCRmiXmzRFTQCXMFEsuh37hJL2rQtD3iok5dmNSKBu2cTqOgExsGC3md7k+5AgtFg1g3kFbrLquxzbgVc6/msBII7oQ2G8uLjDfWWBu02etWORgm7PVCzoYhx4OI3W7E68WIN50qz2suDxKH9aaPlNR9arFHt50Qhy7VoESt6tIZDlheH9Ed9JlvRNRC52KLkzJJXKYe2CS1MlFoEwQ2nmdS8S2kHdNmmisykhU2M1lGaZWNtMpEK/DODDi3qrwyXQ4Ujadylv2UyiilMJJ/56asMk6rbKZVpF3bYaeos6WoHMxmOJ1JcXItxbOrNzhXNL7rBt8uCL7nipzrgomqMVVUpkqWrT9ID80S2/kCEzXL45TC0XWZw+syR7LKy2yO9+ocZ1qes2yOj9kch4rG5q/z5C+kR6LEzlyBiazw9sosX6QZPktX+TKj8iZf5rTo8CKlsZNS2JrNMJXVf5LGfpmRLdiwDJ5oGi9SaU7lDK8yKq8bCR87Lb7WYz41Yj5UfQ5tnft6nrVLSK2hR/tWhdvrHborVVpLFforEZ2ViNH+gN3jZXZPVpkeLHFrrUlvyaU19C8lNXoeC32fpbst2oOAZt+jOwhp9V3u3Y+YTuYZj+psb8/TW/RJui7NnkvjElIUhVQqPmEUUipZWJaJ63h4no0wDQwh0IWBsASmKTCFQJiXk3zfx3EcgiDAsiwMw6AW1inZDrquIwxx4Wf/P34A7D9wv2u7rVEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/4a13a0f8e010f30ad240546ffe7bf45b/2a195/04-angularJSONEslint.png&quot;
        srcset=&quot;/static/4a13a0f8e010f30ad240546ffe7bf45b/8ff5a/04-angularJSONEslint.png 240w,
/static/4a13a0f8e010f30ad240546ffe7bf45b/e85cb/04-angularJSONEslint.png 480w,
/static/4a13a0f8e010f30ad240546ffe7bf45b/2a195/04-angularJSONEslint.png 620w&quot;
        sizes=&quot;(max-width: 620px) 100vw, 620px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Create a &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt; file based on your &lt;code class=&quot;language-text&quot;&gt;tslint.json&lt;/code&gt; file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The content of the file will be similar to a &lt;a href=&quot;https://github.com/angular-eslint/angular-eslint/blob/master/packages/integration-tests/fixtures/v1014-multi-project-manual-config/.eslintrc.json&quot;&gt;sample configuration here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I recommended you to read the full &lt;a href=&quot;https://github.com/angular-eslint/angular-eslint#notes-on-eslint-configuration&quot;&gt;Notes on ESLint Configuration&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 873px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6803387d54369296aabfd0dca9166043/35751/04-packageJSON.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABpklEQVQoz1WS226bQBRF+YwoxomH22CwB4a5AI4MxI5N06Rq//9nVuXpUx+WtkZntHW29ol+rWfMaPHO0/c9XWdomhalGpxzXC4XjDGM48DtdmMYBvreB9W6pWnbMFdKcTweiZwfaFTLNM0BYyxpmpEkCUmSkueSp6cNm80LWSbZ7dKAEBnb7Y7tJmb3+hI03sREznS0bRM2meeZ8/nMNE1hQykFztd4XzOMNdZKjMnROsfYAq0zdkVGLBJeCsFWvBJ9/PimqjveTlOInOcFUpbUB0XdJvwc93y7kt+nksspo7OvGCPwfUrfpyhvkaqh8oq8kkTz5x/6fcfXfGX9/AyRH4bVvqDrT8zXFe8GvB+xtg+qlKaqFE3TsS9LEiHIkhSxE0S96zgUBYPznM9TMCzLkr3MqVvN+vXNut653++8v7+HYrx31HWNUsfwN89zpJRkWUb0dv1iXytkIamqOkTOsjwUIw8VZvThApz/dwUPrLW0bcvhcAhmD4qiIE1TomFZKatjeDyMHjxMU5GQao1dFi7LEopaliWgtUYIwfPzM3Ec/8dfEVP4IVpOKSUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/6803387d54369296aabfd0dca9166043/35751/04-packageJSON.png&quot;
        srcset=&quot;/static/6803387d54369296aabfd0dca9166043/8ff5a/04-packageJSON.png 240w,
/static/6803387d54369296aabfd0dca9166043/e85cb/04-packageJSON.png 480w,
/static/6803387d54369296aabfd0dca9166043/35751/04-packageJSON.png 873w&quot;
        sizes=&quot;(max-width: 873px) 100vw, 873px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, if you run &lt;code class=&quot;language-text&quot;&gt;npx ng lint&lt;/code&gt;, it will use ESLint under the hood.&lt;/p&gt;
&lt;p&gt;⚠️ &lt;strong&gt;I faced a strange error when running this command&lt;/strong&gt; on &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/pull/89&quot;&gt;another project&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;Invalid rule result&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Instance of class Promise&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 714px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6e52f98b79a09e3c5824378ab3597fb2/d67ca/10-eslint-error.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABU0lEQVQoz3WQ3Y6bMBBGeY1ie0wghJ8YWBrBsqYiS1r1rlK3UnvV93+NU5lss1GrvTiaz9LM0SdHbdexLAuP08TkPeMwbPk0DAzDwJP3DOPIOD3ivWeeZ2bvt51jf6L5eMI5R9u2ZFlGdOl7fn/+wsvyzK/nlW/zJ34uZ178zNdh5Mdy5vs4sT70NF23HfZ9T3102MqRFBWJtSRJgogQHfKcpq4pi4K6LMn3e+qqotjnZGlGeTiQpSnmQ4yJY1Qco8NUChPQCmPMjchozV4pUqXZKU2mw1QU2pBrc30bQyGWo1hyEWqxWBGMyNbqnigPi9byIJb2jk4sjVicWFIRKiO4V0IOx6HRf0IdK7RS2NBEm61ZEOzMlcSEJmabuzveFXZdR9M0HJ2jrCryokCH/5CrKCDmLd94lf0rjS6XC+u6cl4WGuew1r4tGLnJ/uYb7zT8A77t5hgWjQn1AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/6e52f98b79a09e3c5824378ab3597fb2/d67ca/10-eslint-error.png&quot;
        srcset=&quot;/static/6e52f98b79a09e3c5824378ab3597fb2/8ff5a/10-eslint-error.png 240w,
/static/6e52f98b79a09e3c5824378ab3597fb2/e85cb/10-eslint-error.png 480w,
/static/6e52f98b79a09e3c5824378ab3597fb2/d67ca/10-eslint-error.png 714w&quot;
        sizes=&quot;(max-width: 714px) 100vw, 714px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I look it up on google but all of the reported issues were related to @angular/devkit version and stuff, which didn’t fix mine.&lt;/p&gt;
&lt;p&gt;I ended up doing two manual steps, as explained above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt; to replace &lt;code class=&quot;language-text&quot;&gt;tslint&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;angular-eslint&lt;/code&gt; for linting configuration. You can copy from this &lt;a href=&quot;https://github.com/trungvose/angular-tetris/blob/1f1fc6b8c960bec010f26862e6a06ac0a7302043/angular.json#L125&quot;&gt;code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;And created a &lt;code class=&quot;language-text&quot;&gt;.eslintrc.json&lt;/code&gt;, and filled in content from what I already have for &lt;a href=&quot;https://github.com/trungvose/angular-tetris/blob/master/.eslintrc.json&quot;&gt;angular-tetris .eslintrc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;step-3-remove-tslint-and-codelyzer-from-packagejson&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-remove-tslint-and-codelyzer-from-packagejson&quot; aria-label=&quot;step 3 remove tslint and codelyzer from packagejson permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3: Remove &lt;code class=&quot;language-text&quot;&gt;tslint&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;codelyzer&lt;/code&gt; from package.json&lt;/h3&gt;
&lt;p&gt;You could either do it manually by removing those from &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;, but remember to run another &lt;code class=&quot;language-text&quot;&gt;npm i&lt;/code&gt; to update your &lt;code class=&quot;language-text&quot;&gt;package-lock.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Or you could run &lt;code class=&quot;language-text&quot;&gt;npm uninstall -S tslint codelyzer&lt;/code&gt; for that purpose.&lt;/p&gt;
&lt;p&gt;And then manually delete &lt;code class=&quot;language-text&quot;&gt;tslint.json&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 823px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/88b741ad09dd84506802735a2d18dd50/31aff/05-codelyzer.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB6UlEQVQoz3XQYU+bQACAYf7DXJbaReC4OyhQgYMDrnAcFFoolFpHbbXLNEt0i3OJWeb/z+KW+G3J+/X58kqURpTGrksUBQAAETbgmRz7VIgipHEcMhbNXMcDCgQKVGXwmgIVGaoylNq2W6265bKmNAx8apq2aQAamGke8XkwK8N5m5VdWvWClRFJHcI9wiYxM/wYS6u2zfOcMZbnRZZlURgbU93y9CkxHWqJ44EfdmzbsVXBa55UMc2px8xzim2KpUIUaZoFQWiatiyrhBBbQ66GHFULxiBPGpE2gi3zICvYgntJpmgLFQajs+m7kVQvVn1/EYaxYZgI6VPTJADEmhapanWqbJN6kzTbpNlY/n4yPZrniw8fq5NR9f60PBlJrhe4LtE0BCFGSMdId9W/GKiBrJSiXpX9XNQdn1cTXUCFymfW6cgej6fjsQSA9joZ6f/SoB4SVMzgktttbt183d4+DMNNc3tI7j/z/dWsbfxu6WxKo59PJIwNjI03DKHuxDqd46wh7TXd/T4Ov653T93wY7P+vq5u28WeD3dp2Z/zxpbe2Bt2IxzlOFu67ZV/+bi+fOyHb/XVQ9t+yfuNtf3ksFL3Ehhw9F8satLt/eHndvd8OLzc7V/uds+749P6eC8uBsIrKxToD0KygQP8kzt8AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/88b741ad09dd84506802735a2d18dd50/31aff/05-codelyzer.png&quot;
        srcset=&quot;/static/88b741ad09dd84506802735a2d18dd50/8ff5a/05-codelyzer.png 240w,
/static/88b741ad09dd84506802735a2d18dd50/e85cb/05-codelyzer.png 480w,
/static/88b741ad09dd84506802735a2d18dd50/31aff/05-codelyzer.png 823w&quot;
        sizes=&quot;(max-width: 823px) 100vw, 823px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;fix-all-the-lint-errors-in-angular-tetris&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fix-all-the-lint-errors-in-angular-tetris&quot; aria-label=&quot;fix all the lint errors in angular tetris permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fix all the lint errors in Angular Tetris&lt;/h2&gt;
&lt;p&gt;After having eslint set up, I run &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt; and get many errors.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/34e5db7851bf924b79167e3257a6f6e7/99f37/06-lint-errors.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABNUlEQVQoz21R7a6CMBTbc1xREAGBKbIJC/I5GWDi+79Pb85J9MfN/dG0I6FpT4XWGg+lMPY9rLXouo4xTRP6vkfbthjHEff7HXmeoygKBEGA4/GIJEkQRRFzHMesRS4lqv0B8zBgWVe4eYZzDvM8M8iU3q/Xi/n9fuN6vbJJVVUoy5KZcLvdIDIpofcHOGuxbBuezsE+n19jSvcxJ5BxXdfIsgzUjkwoPWlKL2SaQnsHDMbADgNs13H92hh0bcs/G2O4etM0fA5iqk9JpZRsRJq+CRXF0HGCIjxBhiF0eEIahvCD4HsrAmnf97Hb7Rie5/0LUagStZSw08R1GxrEWq5HI63rygN9QPUulwvSNOXaH6Z0pAU9qh8Pbhh5FDJeloXvR/xXb9vGZzifz8xKKR6ENN3xFwha36ciHeNwAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/34e5db7851bf924b79167e3257a6f6e7/d9199/06-lint-errors.png&quot;
        srcset=&quot;/static/34e5db7851bf924b79167e3257a6f6e7/8ff5a/06-lint-errors.png 240w,
/static/34e5db7851bf924b79167e3257a6f6e7/e85cb/06-lint-errors.png 480w,
/static/34e5db7851bf924b79167e3257a6f6e7/d9199/06-lint-errors.png 960w,
/static/34e5db7851bf924b79167e3257a6f6e7/99f37/06-lint-errors.png 1100w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I added one more command on &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; to automatically fix problems if possible.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;lint:fix&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ng lint --fix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After cleaning all of the errors, that’s how it looks.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cfd5810de7a8c27a4ca601124cbed9b7/985dc/07-lint-all-pass.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAIF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAByYAD/8QAFRABAQAAAAAAAAAAAAAAAAAAARD/2gAIAQEAAQUCb//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAAABD/2gAIAQEAAT8hX//aAAwDAQACAAMAAAAQc8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAACAgMAAAAAAAAAAAAAAAAAASFRETGh/9oACAEBAAE/EIPbHiukUf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/cfd5810de7a8c27a4ca601124cbed9b7/6a068/07-lint-all-pass.jpg&quot;
        srcset=&quot;/static/cfd5810de7a8c27a4ca601124cbed9b7/09b79/07-lint-all-pass.jpg 240w,
/static/cfd5810de7a8c27a4ca601124cbed9b7/7cc5e/07-lint-all-pass.jpg 480w,
/static/cfd5810de7a8c27a4ca601124cbed9b7/6a068/07-lint-all-pass.jpg 960w,
/static/cfd5810de7a8c27a4ca601124cbed9b7/985dc/07-lint-all-pass.jpg 1087w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;setup-husky-and-pre-commit-hooks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setup-husky-and-pre-commit-hooks&quot; aria-label=&quot;setup husky and pre commit hooks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setup husky and pre-commit hooks&lt;/h2&gt;
&lt;p&gt;Having lint setup is useless if you have to run it manually every time before you make a commit. Because most of the time, we will forget about running lint part. 😂 Pre-commit checks are commonly used to run linting scripts and tests, allowing each commit to be as clean as possible.&lt;/p&gt;
&lt;p&gt;If we only setup lint to run on CI, developers allow to put &lt;code class=&quot;language-text&quot;&gt;not very clean&lt;/code&gt; code to our branch. Imagine a CI process take 15 minutes to run and when a developer push a commit, and move on to another task. If he realizes the CI has been failed after 10 minutes, he has to switch back to the other branch and fix the code, reducing productivity. We should set up lint on both &lt;code class=&quot;language-text&quot;&gt;pre-commit&lt;/code&gt; hook and CI.&lt;/p&gt;
&lt;p&gt;And you should never use &lt;a href=&quot;https://stackoverflow.com/questions/7230820/skip-git-commit-hooks&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;--no-verify&lt;/code&gt;&lt;/a&gt; to skip the lint process locally.&lt;/p&gt;
&lt;h3 id=&quot;husky&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#husky&quot; aria-label=&quot;husky permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Husky&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/typicode/husky/tree/master&quot;&gt;Husky&lt;/a&gt; make git hooks easy. It can prevent bad git commit, git push and more&lt;/p&gt;
&lt;p&gt;Install husky by running&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt; husky&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then modify the package.json to looks like below.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 695px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/55f5a30564ac10428e2bc6289020e2e0/83b75/08-husky.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABwklEQVQoz43OXU/bMBTG8XwO2oTEdpyXJo6TtE3fIClsKlAY1TTEBJWmbRe7nPb9L/5Tw8TgYmIXPz3Hxz7WcYQQCBGgdEyx6MibFXk9RytFphUjrajTqK+TUGLikDLRfT+PQmysSWONUpKh6+KEYYjWGikUdbuhmp0wM4Zj7xjP83Bdl6PhkMHQ7QcGff3X4b7nef17R0qJChXSD0jrKel0TpZomjxhnMXEoew30zJABj6xEv12h9RS9J+85CgZolSI7wZkTUs268iU4rTIsHGE7x6TSEnoBwjPJwoEI6WIhUT5gsGRy+Bo+JzOqAipJhn1NGKxWbK4OGG5Lnl/Zlh3hqrRjBtN2WjMRJIfjJ/ycLZThW3UU04VzijX2CrDlhHN+YrJesFyZVmvDOM6oigVVaUxZUhaCEaFfOXQe8lJMkVWREzmlkk3pxgnmMOgDUhsQGrFc47+gzPrYs4uay7uDXe/dtz/3LH7tuL6y5gP+4KbP273lu2j4eoh5+rB/JPTtDHtpmJzZ9jua26+n7L78Y6br0t2j5ZP+4qPj2Xv8nPOxRucaqaZtznnW8v5raHdRrTbmG4b014nr3TX6Zt+A4g/OgKqqCVqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrate Angular to ESLint&quot;
        title=&quot;Migrate Angular to ESLint&quot;
        src=&quot;/static/55f5a30564ac10428e2bc6289020e2e0/83b75/08-husky.png&quot;
        srcset=&quot;/static/55f5a30564ac10428e2bc6289020e2e0/8ff5a/08-husky.png 240w,
/static/55f5a30564ac10428e2bc6289020e2e0/e85cb/08-husky.png 480w,
/static/55f5a30564ac10428e2bc6289020e2e0/83b75/08-husky.png 695w&quot;
        sizes=&quot;(max-width: 695px) 100vw, 695px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt; will always run when I try to commit a code.&lt;/p&gt;
&lt;h3 id=&quot;test-husky&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test-husky&quot; aria-label=&quot;test husky permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Test husky&lt;/h3&gt;
&lt;p&gt;I defined a variable that doesn’t satisfy &lt;code class=&quot;language-text&quot;&gt;camelCase&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;UPPER_CASE&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Test_Var&lt;/code&gt;. When trying to commit the code, husky prevents me from committing because lint doesn’t pass.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/cc213f24f2e16686cdd5fd0779cda7d1/09-husky-test.gif&quot; alt=&quot;Migrate Angular to ESLint&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Works great.&lt;/p&gt;
&lt;h2 id=&quot;takeaways&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#takeaways&quot; aria-label=&quot;takeaways permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Migrate from TSLint to ESLint is simple with @angular-eslint.&lt;/li&gt;
&lt;li&gt;Set up a pre-commit hook to catch lint errors early and keep the code base clean.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;homework&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#homework&quot; aria-label=&quot;homework permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Homework&lt;/h2&gt;
&lt;p&gt;Notice that &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt; will lint all the files in our application, which we only want to run on CI. We want to run lint only on modified files or on &lt;code class=&quot;language-text&quot;&gt;staged files&lt;/code&gt; for local development. It will enhance the development experience a lot more. It would be best if you considered doing some research about it. Let me know in the comment what is your solution :)&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/angular-tetris/pull/12&quot;&gt;https://github.com/trungvose/angular-tetris/pull/12&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Capture picture from your Webcam in Angular]]></title><description><![CDATA[A simple walk through how to use the browser MediaDevices for capturing picture from a webcam in Angular]]></description><link>https://trungvose.comcapture-webcam-picture-angular/</link><guid isPermaLink="false">https://trungvose.comcapture-webcam-picture-angular/</guid><pubDate>Sat, 16 Jan 2021 04:15:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;use-case&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#use-case&quot; aria-label=&quot;use case permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Use case&lt;/h2&gt;
&lt;p&gt;If you trade crypto often, you will notice that their verification process involves a step to upload a photo of you with your identity card. Or you can capture it via webcam. Refer to the below picture taken from &lt;a href=&quot;https://www.binance.com/&quot;&gt;Binance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e8a67c6ebed60450306e9f579ca73797/7161f/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAABqUlEQVQY0x3NTW+TYAAAYP4YvRAGpi2ztbC1aynUMtKYLZmZMfFq4tHFu/HmYWcnMHdwc5potoNxYVZayme7DkfpoJS2UF5Tf8CTB/r2/aLdMeS2KrdVw7Qty7LtQRAEmqavodkMjLx9/046lY7EDx+FI0EQREGUBOlEPJGET5DS1aaR7/ve+H48CSfzebyYhQAAzxvjWB6GEYRFUQbBKfRBAc0TeCGXp4hipbBJkwyk/L4IL6mksx+5X8eOkgIQBPdxvHDvXGyFUYLLllpEqb5ObT0sbxarGyS7Udku11tVDlKvT52r1wcHb4TDV0nUT1KQJAkAYHTn4MVWJsuTfK66S9a4RzRDsjS1XSu36NoO09hjeajX6wau8nT/hXj4Mg7NFIB0uVxhd4RhBJxBt/j15i7Z5EvcY+pJs77DVPYY+lmDfd7goD9Ke+p8WXrnfv848EcpAIv4/zzycCyXgZGz88/DoanrXdNQNVmyNMXWtYGhmaoK/bj82TVuZcW67vTtwe3N0LHsG+ev2+vp2NoKX/2S0xRMwmkURZ59Fob+NJpHs5kfBP8ArrQcy/kH+jwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Capture picture from your Webcam in Angular&quot;
        title=&quot;Capture picture from your Webcam in Angular&quot;
        src=&quot;/static/e8a67c6ebed60450306e9f579ca73797/d9199/01.png&quot;
        srcset=&quot;/static/e8a67c6ebed60450306e9f579ca73797/8ff5a/01.png 240w,
/static/e8a67c6ebed60450306e9f579ca73797/e85cb/01.png 480w,
/static/e8a67c6ebed60450306e9f579ca73797/d9199/01.png 960w,
/static/e8a67c6ebed60450306e9f579ca73797/7161f/01.png 1182w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After selecting the Take Photo option, it will ask for your permission to use the webcam. If you allow, then you can start to take a photo with your webcam.&lt;/p&gt;
&lt;h2 id=&quot;output&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#output&quot; aria-label=&quot;output permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Output&lt;/h2&gt;
&lt;p&gt;We will build something similar to the output below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d71ce235b12effe7affd94a9d2013ce8/output2.gif&quot; alt=&quot;Capture picture from your Webcam in Angular&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;web-api-mediadevices&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#web-api-mediadevices&quot; aria-label=&quot;web api mediadevices permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web API MediaDevices&lt;/h2&gt;
&lt;p&gt;The MediaDevices interface provides access to connected media input devices like cameras and microphones and screen sharing. In essence, it lets you obtain access to any hardware source of media data.&lt;/p&gt;
&lt;p&gt;We will utilize the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices&quot;&gt;MediaDevices&lt;/a&gt; for accessing the webcam. Some methods worth mentioning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;navigator.mediaDevices.enumerateDevices()&lt;/code&gt;: requests a list of the available media input and output devices, such as microphones, cameras, headsets, and so forth.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;getUserMedia()&lt;/code&gt;: with the user’s permission through a prompt, turns on a camera and/or a microphone on the system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It will ask for your permission, similar to the below photo.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 435px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3623f1dc5e939fb4efea18cd3124ce88/330eb/permission.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.83333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8ElEQVQoz42P2XKjMBBF+RFPYrNoQUJCSIDBYjObBDiJ7alJzf9/yEzsvM90neq6L6dutzOg8GcHwQAA5Sxrh8FM8zZOizFb/5VXYy+66spTE1KGcLg/eK9794kjMMkFgQLzJFenwdjLOG9tN01ma7pxGJdpXkvdHssmYglEoeuBgxs8cbpxyYouS6tl/dC6+fX5e57tNFtj15Ouq7qLhRSJkipjXMRCpVnBhRQylSp3Zvtul/fuPJvlvW5HxhWNkohJQgUOeUhiiBl6EEAKHviAhFSIJHOmcbJ2q+r+3JvjsUpkcb19Xt7us7kM49qdzXa5Les1kUUAKETsSUhELHIn6W663z7eto/rtW3PaV7lx/p0attuqpt+GJenhjCHiH/LmAcgCgl3pDanpp+n0VpbFCWNZMRTqYo001IVWa4jpgCMACQAEIQZRBEA4aOcO9aMizUQ4iw7EsL8gAIY+QF5QP2AABghzLN6U+WMEOWJLrobJklImLOsa9/3AKDzeeSxBDCCiD2O/H4PwK9N44LyI0RRSBMmNCZJEGBHKZXnOcZh03Rpmrtu4HvA96HnAc8Drhs8ORz8w8F3XfDI3svLfrf74Witq6oqimLbtqqqpJTpf8x+/7rb7b6a4zguy/J+v/f9oJTK/jUPef9X/gP+35ZxRtm8mwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Capture picture from your Webcam in Angular&quot;
        title=&quot;Capture picture from your Webcam in Angular&quot;
        src=&quot;/static/3623f1dc5e939fb4efea18cd3124ce88/330eb/permission.png&quot;
        srcset=&quot;/static/3623f1dc5e939fb4efea18cd3124ce88/8ff5a/permission.png 240w,
/static/3623f1dc5e939fb4efea18cd3124ce88/330eb/permission.png 435w&quot;
        sizes=&quot;(max-width: 435px) 100vw, 435px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;webcamsnapshotmodule&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webcamsnapshotmodule&quot; aria-label=&quot;webcamsnapshotmodule permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebcamSnapshotModule&lt;/h2&gt;
&lt;p&gt;I started by creating a new fresh:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;WebcamSnapshotComponent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;WebcamSnapshotModule&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SCAMs (single component Angular modules)&lt;/code&gt; are Angular modules scoped to a single declarable, and you should follow that to create reusable pieces for your application.&lt;/p&gt;
&lt;p&gt;Refer to the below step how to do it on &lt;a href=&quot;https://stackblitz.com/&quot;&gt;stackblitz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/f3b41ad14d02a45f24565ddd8d42d99a/02.gif&quot; alt=&quot;Capture picture from your Webcam in Angular&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;One additional step I need to do it to export &lt;code class=&quot;language-text&quot;&gt;WebcamSnapshotComponent&lt;/code&gt; on the &lt;code class=&quot;language-text&quot;&gt;WebcamSnapshotModule&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;CommonModule&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  declarations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;WebcamSnapshotComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  exports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;WebcamSnapshotComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebcamSnapshotModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;webcamsnapshotcomponent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webcamsnapshotcomponent&quot; aria-label=&quot;webcamsnapshotcomponent permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebcamSnapshotComponent&lt;/h2&gt;
&lt;p&gt;Now we have the component and module ready. Let go ahead and write the actual code for accessing the webcam.&lt;/p&gt;
&lt;h3 id=&quot;webcam-snapshotcomponenthtml&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webcam-snapshotcomponenthtml&quot; aria-label=&quot;webcam snapshotcomponenthtml permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;webcam-snapshot.component.html&lt;/h3&gt;
&lt;p&gt;Open &lt;code class=&quot;language-text&quot;&gt;webcam-snapshot.component.html&lt;/code&gt; and put in the code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;video&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[class.show]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;!isCaptured&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[class.hide]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;isCaptured&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;#video&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[width]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;WIDTH&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[height]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;HEIGHT&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;autoplay&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;video&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;canvas&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[class.show]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;isCaptured&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[class.hide]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;!isCaptured&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;#canvas&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;canvas&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[width]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;WIDTH&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[height]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;HEIGHT&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;canvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;snap-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn btn-primary&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;!isCaptured&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;(click)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;capture()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Snap Photo
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I have a &lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; tag for displaying the webcam stream. Also, a canvas to render the image after taking the photo with a button name &lt;code class=&quot;language-text&quot;&gt;Snap Photo&lt;/code&gt;. Please be noted that both &lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;canvas&lt;/code&gt; have the template reference by using &lt;code class=&quot;language-text&quot;&gt;#&lt;/code&gt; to access it using &lt;code class=&quot;language-text&quot;&gt;ViewChild.&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;webcam-snapshotcomponentts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webcam-snapshotcomponentts&quot; aria-label=&quot;webcam snapshotcomponentts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;webcam-snapshot.component.ts&lt;/h3&gt;
&lt;p&gt;Now let look at the code that does the heavy lifting part.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebcamSnapshotComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AfterViewInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;WIDTH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;640&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;HEIGHT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;480&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ViewChild&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;video&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; video&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ElementRef&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ViewChild&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;canvas&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; canvas&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ElementRef&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  captures&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  error&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  isCaptured&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ngAfterViewInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setupDevices&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupDevices&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getUserMedia&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaDevices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          video&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stream&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nativeElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stream&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nativeElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;You have no output video device&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;capture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImageToCanvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nativeElement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;captures&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nativeElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toDataURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;image/png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isCaptured &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;drawImageToCanvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nativeElement
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WIDTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;HEIGHT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;WIDTH&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;HEIGHT&lt;/code&gt; const for the webcam and canvas size&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;video&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;canvas&lt;/code&gt; refer to the template using the template reference. By default, you can only access them after the view has been init using &lt;code class=&quot;language-text&quot;&gt;ngAfterViewInit&lt;/code&gt;. That’s why I implement &lt;code class=&quot;language-text&quot;&gt;AfterViewInit&lt;/code&gt; interface instead of &lt;code class=&quot;language-text&quot;&gt;OnInit&lt;/code&gt; hook. See more about &lt;a href=&quot;https://angular.io/api/core/ViewChild&quot;&gt;that&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;setupDevices&lt;/code&gt; is where I start to ask for permission using video. &lt;code class=&quot;language-text&quot;&gt;getUserMedia&lt;/code&gt; return a Promise. That’s why I am using async/await. You could still use &lt;code class=&quot;language-text&quot;&gt;then&lt;/code&gt; if you are comfortable with that. Noted that the returned &lt;code class=&quot;language-text&quot;&gt;stream&lt;/code&gt; could be null if your device has no video output device.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;capture&lt;/code&gt; is to take the current video screenshot and draw it into our canvas using &lt;code class=&quot;language-text&quot;&gt;drawImageToCanvas&lt;/code&gt;. At the same time, I also push the data into the &lt;code class=&quot;language-text&quot;&gt;captures&lt;/code&gt; array. It is just base64 encoded image data. You could use that for uploading to the server if required.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 552px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9fb0b9f5b62acad61c84a76074d75f42/08c0b/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABdklEQVQoz01R2XKCQBDk/38mlcMjMYeJN6AoYrwPlIgli1wCykundodYeZjq7undnj2k0POQ+R4yjq5LyOt0wvVwwPV4xNVxcHVyZMQzlwmdMZb71Jcix0H6Y+FiWUj/VbLZ4DweI14uEC/mAhPBCZP1mvh6RXq5RDyfQwpsmzbNpoinE6rZFJExRCB3EA76CDUNodZD2NcQ6QPBuc89ofu5r/UgRbaN82yGSNcRGQaioX5b7LeaCBSZgntdBLJMWpEppNfN+x0xNOiqkHzLwnk6wfl7RDUyBPJpXr2GsKuKDbegXPPAQFUokHuqIgZKibWHr6rwqh8iwPuswqt/4fT+Buf+Ds7TA1ixAFYqghUeiRcLcF8rcJ/LYOUi6coLWLkEKY0yhGsTCX83/iZDXUzlp+ST/XqNsFGnk3baCNotgX/XFX6zgVBVIF1OPhLbxsXe48KR/7i9R7rbIVmtkJqm+FGBmw0SkyrdbZFuTaTbregTN/ELgntCZGzebLcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Capture picture from your Webcam in Angular&quot;
        title=&quot;Capture picture from your Webcam in Angular&quot;
        src=&quot;/static/9fb0b9f5b62acad61c84a76074d75f42/08c0b/03.png&quot;
        srcset=&quot;/static/9fb0b9f5b62acad61c84a76074d75f42/8ff5a/03.png 240w,
/static/9fb0b9f5b62acad61c84a76074d75f42/e85cb/03.png 480w,
/static/9fb0b9f5b62acad61c84a76074d75f42/08c0b/03.png 552w&quot;
        sizes=&quot;(max-width: 552px) 100vw, 552px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code-and-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-demo&quot; aria-label=&quot;source code and demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and demo&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-webcam-snapshot-taken?file=src/app/webcam-snapshot/webcam-snapshot.component.ts&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Please open this &lt;a href=&quot;https://stackblitz.com/edit/angular-webcam-snapshot-taken?file=src/app/webcam-snapshot/webcam-snapshot.component.ts&quot;&gt;stackblitz&lt;/a&gt; in a new window for testing. If it is not working, try click Refresh on stackblitz.&lt;/p&gt;
&lt;p&gt;It happens to me as well :(&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/9c15de4e20d89c8399816ad7f50ded55/camera.gif&quot; alt=&quot;Capture picture from your Webcam in Angular&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You just saw how to use Angular to capture images from the webcam using a web browser. You can always use a library for that purpose. But it is essential to understand how things work under the hood :)&lt;/p&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://x-team.com/blog/webcam-image-capture-angular/&quot;&gt;Webcam Image Capture with Angular&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Observable for Angular Output]]></title><description><![CDATA[Do you know you can use Observable for Angular Output?]]></description><link>https://trungvose.comobservable-for-angular-output/</link><guid isPermaLink="false">https://trungvose.comobservable-for-angular-output/</guid><pubDate>Sat, 09 Jan 2021 04:15:00 GMT</pubDate><content:encoded>&lt;p&gt;I have one suggestion for you guys for using &lt;code class=&quot;language-text&quot;&gt;Observable&lt;/code&gt; for &lt;code class=&quot;language-text&quot;&gt;Output&lt;/code&gt; in Angular. I thought you might not know about that because it wasn’t get mentioned in the documentation. Take a look at the below code and and I will explain on the later part.&lt;/p&gt;
&lt;h2 id=&quot;before&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#before&quot; aria-label=&quot;before permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Before&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Output&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; someEvent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;someSource$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;/* additional logic transformation */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;someEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;after&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#after&quot; aria-label=&quot;after permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;After&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Output&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; someEvent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;someSource$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* additional logic transformation */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, the before and after code was pretty much identical. Its use-case is applicable when you want to use &lt;code class=&quot;language-text&quot;&gt;Output&lt;/code&gt; to emit &lt;strong&gt;something&lt;/strong&gt; for the &lt;strong&gt;Parent Component&lt;/strong&gt; when &lt;code class=&quot;language-text&quot;&gt;someSource$&lt;/code&gt; emit data. But why it works?&lt;/p&gt;
&lt;p&gt;To go a bit deeper, you need to understand two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Event Binding syntax &lt;code class=&quot;language-text&quot;&gt;(eventName)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;class &lt;code class=&quot;language-text&quot;&gt;EventEmitter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;event-binding-syntax-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#event-binding-syntax-&quot; aria-label=&quot;event binding syntax  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Event Binding Syntax ()&lt;/h2&gt;
&lt;p&gt;Event binding is what you see when using a parenthesis and a event name on the template part, e.g. &lt;code class=&quot;language-text&quot;&gt;&amp;lt;child (someEvent)=&quot;invokeSomeEvent()&quot;&gt;&amp;lt;/child&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Basically, Angular Compiler will parse all the template to Abstract Syntax Tree (AST) so that I can have all information regarding the current template. For the event binding, Angular knows which event get binding on the template itself.&lt;/p&gt;
&lt;p&gt;If you look at source code of &lt;a href=&quot;https://github.com/angular/angular/blob/6a9e3284329baf1bb6b621a8840ace629eb4b16e/packages/compiler/src/output/output_interpreter.ts#L166&quot;&gt;output_interpreter&lt;/a&gt;, there is one path to check if the current event is &lt;code class=&quot;language-text&quot;&gt;SubscribeObservable&lt;/code&gt;, it will automatically subscribe to that &lt;code class=&quot;language-text&quot;&gt;Observable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 651px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/74ecf7bf82c905d191d12b4ff8077833/aeb8d/01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 78.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHkZkQg/8QAFxAAAwEAAAAAAAAAAAAAAAAAAAERIP/aAAgBAQABBQIdz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAABAhAAExEf/aAAgBAQABPyF3k4aUP//aAAwDAQACAAMAAAAQPP8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGhABAQEBAAMAAAAAAAAAAAAAAREAMRBB8f/aAAgBAQABPxARKambxS+zVerrAFOZPh8f/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Observable for Angular Output&quot;
        title=&quot;Observable for Angular Output&quot;
        src=&quot;/static/74ecf7bf82c905d191d12b4ff8077833/aeb8d/01.jpg&quot;
        srcset=&quot;/static/74ecf7bf82c905d191d12b4ff8077833/09b79/01.jpg 240w,
/static/74ecf7bf82c905d191d12b4ff8077833/7cc5e/01.jpg 480w,
/static/74ecf7bf82c905d191d12b4ff8077833/aeb8d/01.jpg 651w&quot;
        sizes=&quot;(max-width: 651px) 100vw, 651px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;What does this mean? If event is either &lt;code class=&quot;language-text&quot;&gt;Observable/Subject&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Event Binding syntax ()&lt;/code&gt; will automatically do the subscribe.&lt;/p&gt;
&lt;h2 id=&quot;eventemitter-class&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#eventemitter-class&quot; aria-label=&quot;eventemitter class permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;EventEmitter class&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;EventEmitter&lt;/code&gt; is a subclass of &lt;code class=&quot;language-text&quot;&gt;Subject&lt;/code&gt; under the hood. And as you might have already known, &lt;code class=&quot;language-text&quot;&gt;Subject&lt;/code&gt; acts as both &lt;code class=&quot;language-text&quot;&gt;Observable&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Observer&lt;/code&gt;. With the code on the above &lt;a href=&quot;https://github.com/angular/angular/blob/6a9e3284329baf1bb6b621a8840ace629eb4b16e/packages/compiler/src/output/output_interpreter.ts#L166&quot;&gt;output_interpreter&lt;/a&gt;, if you change an &lt;code class=&quot;language-text&quot;&gt;EvenEmitter&lt;/code&gt; with a Subject/Observable, it will be automatically subscribed.&lt;/p&gt;
&lt;p&gt;When you understand the two concepts &lt;code class=&quot;language-text&quot;&gt;Event Binding syntax&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;EventEmitter&lt;/code&gt;, I hope you get why the above code works.&lt;/p&gt;
&lt;h2 id=&quot;some-use-case&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-use-case&quot; aria-label=&quot;some use case permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some use-case&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Interval Output&lt;/li&gt;
&lt;li&gt;State Selector (ngrx)&lt;/li&gt;
&lt;li&gt;… Anything related to streams…&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example&quot; aria-label=&quot;example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example&lt;/h2&gt;
&lt;p&gt;Angular team also use this approach for one of their search-box component example. Thanks &lt;a href=&quot;https://twitter.com/chandlerfang/status/1348654539067387904&quot;&gt;chandlerfang&lt;/a&gt; for the input!&lt;/p&gt;
&lt;p&gt;View the &lt;a href=&quot;https://github.com/angular/angular/blob/74175f84ea1892cdf6e584d80297cc4c5dd6f6f6/aio/src/app/search/search-box/search-box.component.ts#L34&quot;&gt;source code&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aio-search-box&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    &amp;lt;input
      #searchBox
      type=&quot;search&quot;
      aria-label=&quot;search&quot;
      placeholder=&quot;Search&quot;
      (keyup)=&quot;doSearch()&quot;
    /&gt;
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchBoxComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AfterViewInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; searchDebounce &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; searchSubject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Subject&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Output&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; onSearch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchSubject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;distinctUntilChanged&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;debounceTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchDebounce&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;doSearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchSubject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Have fun!&lt;/p&gt;
&lt;p&gt;Thanks &lt;a href=&quot;https://github.com/nartc&quot;&gt;Chau Tran&lt;/a&gt; for the &lt;a href=&quot;https://github.com/angular-vietnam/100-days-of-angular/blob/master/Day044-output-observable.md&quot;&gt;original Vietnamese version&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[10 Modern CSS layout and sizing techniques]]></title><description><![CDATA[Learn a few layout tricks you can implement in your codebase today, and be able to write entire swaths of layout with just a few lines of code.]]></description><link>https://trungvose.com10-css-modern-layout/</link><guid isPermaLink="false">https://trungvose.com10-css-modern-layout/</guid><pubDate>Fri, 25 Dec 2020 16:16:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;10-modern-css-layout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#10-modern-css-layout&quot; aria-label=&quot;10 modern css layout permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10 Modern CSS layout&lt;/h2&gt;
&lt;p&gt;This video from &lt;a href=&quot;https://twitter.com/Una&quot;&gt;Una&lt;/a&gt; is amazing. You have to watch it. Below was the summary that I took from her video. You can try it live on &lt;a href=&quot;http://1linelayouts.glitch.me/&quot;&gt;1linelayouts.glitch.me&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;10 Modern CSS layout and sizing techniques highlight just how robust and impactful a single-line of styling code can be.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://1linelayouts.glitch.me/&quot;&gt;http://1linelayouts.glitch.me/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=qm0IfG1GyZU&quot;&gt;https://www.youtube.com/watch?v=qm0IfG1GyZU&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;1-super-centered&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-super-centered&quot; aria-label=&quot;1 super centered permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Super Centered&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;place-items: center&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8fe2168108caf999eba467397bc8899f/061c7/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACDUlEQVQoz3WSWU/bQBSF8/9/Qh8opSmgNiKL8gIPlfoCIZuhQuoCZN+I48SOndgee+arZpwEKrUjfbrnjjVnzsg3VygUKJVKFIvFDK3fUCqXKWsqFaNLO12pVMy+PqupVqvk83ly6zgmVIpQSjZpii+EQe/vdWBIdvW13yQp21SyEQkJUGs0yM1WSyaLBSPbZu66+FGEH4ZZNYS4Ychqu31DyCrcYgcek9WCkTPHExG3zRa5ruNw0bL43Gzx9fcjXT/gyfV48tYH7O2WdRThxbEx98KIl7VDZ/5Mx37mcfYLVwSZYWPpkh/P+TJdcD6ekx9MDZ92fOxP6Kx9hIhxfZ9ICBIpieKYOBboJaUyT77RT67NHY77E053BmfDqdGnw5lBG/aDDYHnMRqNcF2XRGRGeikUUqbEe0OdUBueD6acjBd8u7/l8scDJ2Obs13a7to3R6VSpFIipUQpdUD32vC6XifXXHl80IY60eiFi06PQm/I6fCFs+HMpO75wS7Nv5eSErE3vLYd3nXHxlQnPRrMeD+YGn3cn3LUG/NsEpKlU+ovhJSEQpjRM4Y/nSU1e0nddqgf6qu+tR0mfoCnRyfMRugtM99n4Lp4ScKNNtSzpm+J0/S/bJIkG3Jdjd4jzDc96GL/U65rNZqWRb3VotFuH2i2raxaFu27O0PLsgz73rq/P+x/f3jg8uqKP2EubVneHLTjAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/8fe2168108caf999eba467397bc8899f/d9199/01.png&quot;
        srcset=&quot;/static/8fe2168108caf999eba467397bc8899f/8ff5a/01.png 240w,
/static/8fe2168108caf999eba467397bc8899f/e85cb/01.png 480w,
/static/8fe2168108caf999eba467397bc8899f/d9199/01.png 960w,
/static/8fe2168108caf999eba467397bc8899f/061c7/01.png 1216w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-the-deconstructed-pancake&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-the-deconstructed-pancake&quot; aria-label=&quot;2 the deconstructed pancake permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. The Deconstructed Pancake&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;flex: 0 1 &amp;lt;baseWidth&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 906px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d0c7f7f8efac8ac10eb6a6dcfb093748/6029f/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB+0lEQVQoz62S6XLaMBRGef/X6d4mwxbIkIATNhsa2oalYBPAeJEEUuzTsaBT+j+a+Ub3Spqj716p1BsMGLgu/bMGl/I8hqORlTsen2JvhDsaX+QeA89lPJnw8fNnSrzhaDQalIQQrOYrVrMli+cFL/6GJEpPJ/KT8jwnzzKyLLNzkb9mhvQQsdmvWe9WvOaGm5sbSiY3jKddhj/bDCcO0/CJ38kzOx2wPi7Yah+TvVp2MbICnkOURiy2U1bRkul2ikGfgEcUg2ONni7z3bToqTJ9VWEg63TSLwxlnVQL1lHMPAjYJDHx8UgoJZE8Eh8MkTray+r1OiWVCfqiymN6jSua9EQBq+HJJo/iCk81EUYx8wOGT0/8mM/xw5DocLDgvVLspLAVWKDmQC+t0Im+MlK3PETXdJMynrrlLvyEJxukWrLahSy3W4L9nnUUEYQhL3FswYXb/4GiQldcW1d9WTisXjhsIIxk6gdM5jN+LZcsdzumvs8sCAiFYC/lv5Jlllrgg7jClU26omxb4MoGjvhm56KH8aHolyLVGlHIGKui5I1IyYBarUZJG01qYhK9R5iEREekOj7HexsXZ7Cve6Hz91HGECtlHVrgW37sSrVKqdlqcdfp0Grf02q3z3GbVqd9yp0O945jVewV+pvfOw/cndedbpd37z/wBzYmfgMqqFb5AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/d0c7f7f8efac8ac10eb6a6dcfb093748/6029f/02.png&quot;
        srcset=&quot;/static/d0c7f7f8efac8ac10eb6a6dcfb093748/8ff5a/02.png 240w,
/static/d0c7f7f8efac8ac10eb6a6dcfb093748/e85cb/02.png 480w,
/static/d0c7f7f8efac8ac10eb6a6dcfb093748/6029f/02.png 906w&quot;
        sizes=&quot;(max-width: 906px) 100vw, 906px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;3-sidebar-says&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-sidebar-says&quot; aria-label=&quot;3 sidebar says permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Sidebar Says&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;grid-template-columns: minmax(&amp;lt;min&gt;, &amp;lt;max&gt;) ...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 902px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5d19fab2f2cc2e7ee74e7a2ea8489ab5/58213/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACEklEQVQoz32TWW/TQBhF8//f+QMUISHxiCitQIg2W52krcCxG8du0mZpNjuxMxPH6wGP000gLB3d+1meqxnNdcUZDun1+/RsG8t2ShynnB2H/u2twh4MXvnnufjeZjgacVatUsmAPTkRECryJ2SWIdO01AM7panSMId9DmEGRY52eUll6m64uNS50m+YLFYs1j5zb6PU3+8JkohNGLLe7fAOrHchKxkw3jzgzAf0pjZuuKXZ7lBZzQ2uz99xXf3G2XGb6ommqJ22uWqYWPo9ri+QaYJMEkQcK9xgzcQdsRQLZv4UmUU0Wm0q/qzG0njDoPOF4w8/OP34neMPX6l+0mh87tA6+Ym3FmzFltlshhCCJE2I44Q0ycgzFClQb7WoxP4l+ew9y14dqznF1u7pX9xjayOc1phe8w5vI5BS4LouQRAQhiFSSkXhoygifgyMNh2SyRELs4pZG2E1h/Qaz5j1Ad56y/+eLMsOgVoZmE6OWN7UuKmPVaDVvHuiDBRqYZ7n/07Mc5KXgfH47dMOe43BXztceVviPGOfJOzTA8qnBFGEKyUiy8pAGUs8/wF34zJ3Axaez8ILDpTek/JVbZ7rs2MhBFM/IEhT6ppGpTi7v48RSVJWQ1EUt6SYhapLWRtVncNc1OexSuqWi0DTNNH1X+i6TrfbfYGh1DAMDNMsKfwjpol5eN81DCzL4vzPr/cbm1J5oRsBRkAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/5d19fab2f2cc2e7ee74e7a2ea8489ab5/58213/03.png&quot;
        srcset=&quot;/static/5d19fab2f2cc2e7ee74e7a2ea8489ab5/8ff5a/03.png 240w,
/static/5d19fab2f2cc2e7ee74e7a2ea8489ab5/e85cb/03.png 480w,
/static/5d19fab2f2cc2e7ee74e7a2ea8489ab5/58213/03.png 902w&quot;
        sizes=&quot;(max-width: 902px) 100vw, 902px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-pancake-stack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-pancake-stack&quot; aria-label=&quot;4 pancake stack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Pancake Stack&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;grid-template-rows: auto 1fr auto&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 902px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d6478d91dc2cd5ecbe78e9c82c4eae19/58213/04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB50lEQVQoz62TWU/bQBRG/f9/QV8q6P5WqaqqqjQhlKWiKU28QoCQEEhlsmCPJ3bGjmOf1nYojaBvHen40x3dOTOWx1pvMOC02+XsouCi5Pwue72Sbr9f8nd9cdmnu6qL3svra/YPDtD4j8O2bTQlA66OTa6ODcKbIZmYgoqqjjwrHpDnZFlGvqKo02yBVD4TcYN7OyQjxTRNtI4IePvjiI+OQ+3slPr5KXvuiKZUHIqIZhARpsvSX+izQp7n+DOf4e0VrnQZ3A5YssQyLbSOc8RB4x2OsY/R2inRv9dpf9uqaNbwpI/reQxcl4mUBHGMP1cEKmEWp4h5Um5oGgZabO0gPjwhqm0Sft4guqO2WWX9OaE/5qfn0xsOuR6NGAlRMpYBQazwouhemHS+Mv20gdd4jbf96iGNN8z8CTJdIpO4PF2g1FreCY1CuDjZJ9h6imy8JKg/u2e7yE2C7RfM/DEiWSDmc4RSa1SvP78XjpwzjPdbdGq7XO421+h/adLfbSKFJExTZkmyRpgk+EoxDsPiLlTCJM5xXZ/pZEbgxw8QniJdVF85z/MHqGKjOK5OqOvVxc5L/79H/kj92IpWu41m2DYtXadlGLRN8w+6aVVpWRQ9Bbplo6+ynHOc1byFfXLC3u9f7xd9wHgMzq1CbQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/d6478d91dc2cd5ecbe78e9c82c4eae19/58213/04.png&quot;
        srcset=&quot;/static/d6478d91dc2cd5ecbe78e9c82c4eae19/8ff5a/04.png 240w,
/static/d6478d91dc2cd5ecbe78e9c82c4eae19/e85cb/04.png 480w,
/static/d6478d91dc2cd5ecbe78e9c82c4eae19/58213/04.png 902w&quot;
        sizes=&quot;(max-width: 902px) 100vw, 902px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;5-classic-holy-grail-layout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-classic-holy-grail-layout&quot; aria-label=&quot;5 classic holy grail layout permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Classic Holy Grail Layout&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;grid-template: auto 1fr auto / auto 1fr auto&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 895px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c6fed18480c1b523e805a83e53eac70c/fcbaf/05.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACZElEQVQoz2WTa4/bRBSG8/8/V0LiQ0FUbRFIQAVLadiWVhVLIJts08TJrnPZS2Injp31fS7O2A/YWVAqRnr0njM6eud2prW4vcWezbDnc6ZHNPliwez6umFe60Nex/ObG2bXi6Zmuphzu1xy1unQKgEJqCNEBbICYUpyY8jL8lOMQZRVU6Pq2hIMcP7hA607f0N/1GV0ecHA6jKZDggiFz/eECpBqCShFITiGEmQx6yiNXPvhqk7J9Q53X6f1v3S5a7bwRtbrC/HeFcTPPsSz54SLUPCVUiUC7J9Qb7fk2pNpgu2kc9qd0eQ+6wjh9Qo/qoN389uedYdM+z12bS/Z/PmhOz3U+J+m/XgBZ71EqFS4iRlu90ilcKUBmNKqvq+KihN1SzUGL7ydzy6djg7f0t88hlB+zFx+0uy/hPk6jnp3bcIFZEkGb6/JcsytNZIKZFKHlQKEiUPhm+CkMcrn97gD+Trr4jfPic+fYoYfEO1/QG9/hGhYoJgh+M6zQ6rquJ4mPqRjOHPXo/W613M58sd3f4Z6pcvSE6fkrx6grr4GjbfUTgvEDJE5JI4ihBC/M+QqqKAg2EvHNL23nHh/4a9+okr94Qr5wR7+zOz+CXT6FdCGRHGKV4QEKcpar9HG9OQaM0uz8nK8mB4ryPceIWnfBwV4D7giAAn93HFjnuRE0lFpBSRlP+1z70QbLMMN0lImyOf09JAJDSJLMjUnkwdVBSGXJsmrl+waZeiINXFJ3ndSpnW7IHOeZfWeDLh4/Ajw9GQkWUxskZYloU1th50TF3TUMf/MpkwOZq3bZvOP1/vb1q/d7ub89DiAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/c6fed18480c1b523e805a83e53eac70c/fcbaf/05.png&quot;
        srcset=&quot;/static/c6fed18480c1b523e805a83e53eac70c/8ff5a/05.png 240w,
/static/c6fed18480c1b523e805a83e53eac70c/e85cb/05.png 480w,
/static/c6fed18480c1b523e805a83e53eac70c/fcbaf/05.png 895w&quot;
        sizes=&quot;(max-width: 895px) 100vw, 895px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;6-12-span-grid&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-12-span-grid&quot; aria-label=&quot;6 12 span grid permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 12-Span Grid&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;grid-template-columns: repeat(12, 1fr)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 901px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/590569b7d6650172df05ee30434e1678/0955e/06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4ElEQVQoz62Ta2/TMBSG+/9/A/wAQEN84QMXsVHBtg4khBBNsixp7mmapkmatLnZeVDcbew7WHp1/FryY/scn9nVYoEfhliOg+26rDyX1ZPoeB6O7//VvXef+GlfuF5zfXPDzLLu+F/DNE1mum6Q5zt+aT8xVwblIac47KibGiSPGseRUcqTxhEhB8omI8lDgtShkw0Ta2YYBo4wuNy+51txwWJ3zjx8y4/inNvmO9rxmqi1ERKGUTKMo1LVHImymE21xc9iurHn1jCYabpGEs25015gG2e42kvc+CuJtPE7Da/7TdpHZFWNvlqxiiI2+z2bcs+2qimOLbvDkbYfMBRQ05D6guLiNfbHM/xPr0hDl2oExlNupIRtVeHGMUGasilL0v1eaXc4kNU17TBg6Pp0Q/0+pSNf8pJnhs1zw+JdslVrU74GKcmqCi9ZE+8yBdl3nVLZtspPZyvgUlsqXFfesrbfUPYtRS+oh+GxehNwus2d7+OnqQIUTUPeNCo+AHVNY7ZcnoDH3KQOPoDsQZ4qOgiBEIJeCPWkTgilaf7gq64jrWvEA3D6O/865HhKtnry5/mcledhmCamZamOsaZqep7qBnvyjqO6xg0CvCBQnTHJjyLCOFYxThIur674A5B3PMe2h0XRAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/590569b7d6650172df05ee30434e1678/0955e/06.png&quot;
        srcset=&quot;/static/590569b7d6650172df05ee30434e1678/8ff5a/06.png 240w,
/static/590569b7d6650172df05ee30434e1678/e85cb/06.png 480w,
/static/590569b7d6650172df05ee30434e1678/0955e/06.png 901w&quot;
        sizes=&quot;(max-width: 901px) 100vw, 901px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;7-ram-repeat-auto-minmax&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-ram-repeat-auto-minmax&quot; aria-label=&quot;7 ram repeat auto minmax permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. RAM (Repeat, Auto, Minmax)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;grid-template-columns: repeat(auto-fit, minmax(&amp;lt;base&gt;, 1fr))&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 906px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5d63cc7ec510a5bbcb6f8ed6caab5bf1/6029f/07.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4klEQVQoz22T226bQBCG/f4v0AfofaTc9aJVzlbS1ICpozTGNmAONiYNXmKOC/tV0Fixm4707a+dkX7NaHYH1zc32J7HdD7HWiywbPtIZ7bN3HFYuG6vew7vlr1gGYacX14y0CcTWqAC6gMqBSWKUimKtiVrmg/kbVeDvFE0wMg0GWjjMXa05utoxIVpMvz1yJmhMwkCZvEzT1GEmyRsi4KXPO9Jet3hbQOsaM5j8MQmT7jXDQY/xmOS9QszfcriYcFPw+b+6hFj7HDqeHy2HM7CiKKuyaQkl5KsrknzjE0SIQrBNksolORO0xncGwZ5kCBMl8xa441XPHybMtV8TlYxnyyXL+sYWVWEYUgcxyil6ONNOu1GvtVGfw2zWLCd+aTLiHC6wdZ83KeI8/iF02DD7e+EVAgcx8H3fYQQ5HlOmqZUdYVSLbIzHHWGuoHXWkyKC2bViFa1fQeHFLKmahoOY99ld6r2zVDTGHzXdObKYChOMMuzgzneo5SSum2OjHif9t1w36EvbR52t8xyk7yu+gXsKeua17JEFAVpWR7R5Z+zDF8ItnXNXWeomWbvvqtbMql4lfK/7JrmI281UVX92+32MbgeDnED/+hH/Iu9XB6xzzueh+v7fc5frbi4uuIPQeY3R5vh070AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/5d63cc7ec510a5bbcb6f8ed6caab5bf1/6029f/07.png&quot;
        srcset=&quot;/static/5d63cc7ec510a5bbcb6f8ed6caab5bf1/8ff5a/07.png 240w,
/static/5d63cc7ec510a5bbcb6f8ed6caab5bf1/e85cb/07.png 480w,
/static/5d63cc7ec510a5bbcb6f8ed6caab5bf1/6029f/07.png 906w&quot;
        sizes=&quot;(max-width: 906px) 100vw, 906px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;8-line-up&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#8-line-up&quot; aria-label=&quot;8 line up permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. Line Up&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;justify-content: space-between&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 899px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/01f34d04d854e4fe4129159a90e63619/681f1/08.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB+ElEQVQoz62TTW/TQBCG/dsR/4E7CE5cQELQQwuIwqGt3RSlauIkbr7afNjr2F7X9tq7+4CdUrWcO9Kj+dDq1Wh2xhkFAVe+z2A0emD4yA/HY/zxBH8y6eIun0wYPcrbt8Fsxo/jYxye0bzzc5xcSq5HYxIhKDJJnqZo3WCMwRjdYY15irU0piYrd4TJmrW4wWJwXQ/HWNjchohtQiIykljSaDCABXSLtV3cYqzFGEucxSzFnFW2YipmGAxeK0i9pox7UIzRdwFFfEGdDVHpkGzjopI+hZLcipibMETkOVmtSMuKrKqRShPnJdpYPM/FiRdf6R29JAneEfnv8b68QAzeUCw+su69Ig/eUlUCf77k/LLPeLEgkpJtmhLJjF1ZdF41DZ7n4ajoitXFIXLyi+XyJ9+vvtGfn5BEh0y3H1iFn8jvIla7lMV2wzqOCbOMsBOU7Iqi67odi+u6OCZekF7OqNvvX//m9XzJ5+WcTXJErzpgkB8gi4jpestwOuVWCGTdkFbVA61oa+7ZGQ62pM5DdBlTqx15GVFWOxolUGqDqrYUqkBI2SGrCqnUA7uyJMzzTvCsEwRqbWiM7QZr7mkMaL1nX9NorbH23+rsKep2fcq94Onp8y521+EouGYwGjO4P6P/8YNgT3tq9/gdT+uT2YyTv4J/APnhiJSXtrq2AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/01f34d04d854e4fe4129159a90e63619/681f1/08.png&quot;
        srcset=&quot;/static/01f34d04d854e4fe4129159a90e63619/8ff5a/08.png 240w,
/static/01f34d04d854e4fe4129159a90e63619/e85cb/08.png 480w,
/static/01f34d04d854e4fe4129159a90e63619/681f1/08.png 899w&quot;
        sizes=&quot;(max-width: 899px) 100vw, 899px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;9-clamping-my-style&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#9-clamping-my-style&quot; aria-label=&quot;9 clamping my style permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. Clamping My Style&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;clamp(&amp;lt;min&gt;, &amp;lt;actual&gt;, &amp;lt;max&gt;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c52bb294576ef78b8e923ba30281afe9/b67f3/09.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACDUlEQVQoz2WSW0/cQAxG8///SvvSSlVfKtSXlsJSKCywJOS2yYRkYTeb20zuOVUmgiLV0tFnW7LH1tjYmCZRkiDimK0QmiCKtLq+j7vd6jiMIu07vs+j62J77j/fdbndbLhZrzHCJKEFaiYaraDGETkMWl/997FmmqgnaGYAOY6I+AkjiGP8p5ifvy5YXV6z3pjsjhlZW3OsFQcpOSqlSTVLvJcFQSowYxvHf2D38sw2EhheFKG6jv3LgVjsEdsXdnFGdqwpippuHKjalrJpkV2PbDutaZGTHBIylZNne7KywPQDDEeEDCx2v7pl9e0H3tUj1tk9whQ6X3c9ZSURYUiapgzDa8Vi4whpWXJlbzHccG440feg4lOm5APj7jNT8hEV/9YF8waVUmRZRlmWNE1D13WMcyeg73sOec61E2K4QtAz6Vdi85zo5hPx3Vete2+tC5q+J8sLLMsiiiKKokBK+dZwnvhYFvxxgmXCueFs63OL719WnJ2sWZ3c49/HbxPOK+/3B4qipGlbxnFinCatwzCSS7lMuPF86mGgahpKWb9DaVTbkknFUSrSqiJTiqJpyeqavK6Jy5IgzzlUJZeWj2GJWH9KNfSoafyP1/urp5npLSfHOb/c51xbDx0XDw7G6dXNcvGej2k7mLa7+I6rsf2tjmcs19O4QYgbCOxtgCciwuQZkew4u7P5C0kaPTBEj3OMAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/c52bb294576ef78b8e923ba30281afe9/d9199/09.png&quot;
        srcset=&quot;/static/c52bb294576ef78b8e923ba30281afe9/8ff5a/09.png 240w,
/static/c52bb294576ef78b8e923ba30281afe9/e85cb/09.png 480w,
/static/c52bb294576ef78b8e923ba30281afe9/d9199/09.png 960w,
/static/c52bb294576ef78b8e923ba30281afe9/b67f3/09.png 1338w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;10-respect-for-aspect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#10-respect-for-aspect&quot; aria-label=&quot;10 respect for aspect permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. Respect for Aspect&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;aspect-ratio: &amp;lt;width&gt; / &amp;lt;height&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 899px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0a6e9da0d7bfe4eed286b6d25ece1a2a/681f1/10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAB6klEQVQoz62S227aQBCG8/6P0Beo1KoXVVU1UdKSkBhHKmmjVomNgZQQDHYw4NgG2+zhq3aB0Fzlpiv9moN2v9md2QO/2+XG87jxPW59f6NO59l6QWDlG7uN/aCLOWfjrYJ+H9d1OeA/Lgss8ow/nVvCu4Bo0KNME7So7Qat1FbaWrWLtUbJilU6JZ2NWcSh2b0BruuKYXDDoO/R63k8DPvM8oxMSNZSIg1IK/T2FkprpNaki4hk1GOeDInDvqn+8sknYcSbX7e8H8W8Gz/yYRSRrQVoTVHXhEnCQxyT5DlPVcViVZLWa7K1ZLYqUbsnG5gU0E4DLtIGrczBzS9wntqka2mLGeB9FHEXhoymU+bLpYWmZWltkmVIJfdAIcErPXz9BV+e0FFH/K6az8C8qizIKFosXgdqBc404PP4jKPY5TC64Di53gPrmkEUMZhMmBWFBe1kgPOisD12W7seas15GPPWu+PT8JGPw0cOxzMKc3WtKYXgfjKhOxgQz+csisK2wRRalCXJcmkxjuNsgLUQCK2QKISWSC0Rai/zTWxdM2Wl7OQ12k58JQR5Xb0EIhV2TP9K7wiv5I0vNwXPm00OfjQv+d5wtmrt9W2Tuzp1uTpzaZ+2aDcc2o2W9W3e+Fv9PLvk9PgrfwGmBYRGuMk56AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;10 Modern CSS layout and sizing techniques&quot;
        title=&quot;10 Modern CSS layout and sizing techniques&quot;
        src=&quot;/static/0a6e9da0d7bfe4eed286b6d25ece1a2a/681f1/10.png&quot;
        srcset=&quot;/static/0a6e9da0d7bfe4eed286b6d25ece1a2a/8ff5a/10.png 240w,
/static/0a6e9da0d7bfe4eed286b6d25ece1a2a/e85cb/10.png 480w,
/static/0a6e9da0d7bfe4eed286b6d25ece1a2a/681f1/10.png 899w&quot;
        sizes=&quot;(max-width: 899px) 100vw, 899px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Build an Angular component to display snow ❄️ effect]]></title><description><![CDATA[The holiday session is coming. Let add a simple snow effect to your Angular application ❄️❄️❄️]]></description><link>https://trungvose.comangular-snow-effect/</link><guid isPermaLink="false">https://trungvose.comangular-snow-effect/</guid><pubDate>Sat, 19 Dec 2020 16:13:00 GMT</pubDate><content:encoded>&lt;p&gt;2020 is about to an end, and the holiday session is coming. It was such a particular year for you and me. I have been staying in Singapore for more than eight months without traveling anywhere else. Probably, I will not be able to come back home for our upcoming Tet holiday. But tough times will make us stronger, I believe so :)&lt;/p&gt;
&lt;h2 id=&quot;jira-clone-snow-theme&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jira-clone-snow-theme&quot; aria-label=&quot;jira clone snow theme permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jira clone snow theme&lt;/h2&gt;
&lt;p&gt;I did a quick snow theme for Jira Clone based on an awesome &lt;a href=&quot;https://codepen.io/alphardex/pen/dyPorwJ&quot;&gt;codepen&lt;/a&gt;, written purely with CSS.&lt;/p&gt;
&lt;p&gt;That’s my result -&gt; &lt;a href=&quot;https://jira.trungk18.com/project/issue/2020&quot;&gt;https://jira.trungk18.com/project/issue/2020&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/trungvose/jira-clone-angular/master/src/assets/img/merry-christmas-2020.gif&quot; alt=&quot;Build a component to display snow ❄️ effect with Angular&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;snow-component&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#snow-component&quot; aria-label=&quot;snow component permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Snow component&lt;/h2&gt;
&lt;p&gt;So let go ahead and create a new &lt;code class=&quot;language-text&quot;&gt;SnowComponent&lt;/code&gt;. We don’t need to do anything with that component. The heavy lifting part is the template and styling.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Component &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/core&apos;&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app-snow&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./snow.component.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  styleUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./snow.component.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SnowComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Open the scss file, and paste the below code. Basically, each snowflake will have a random position, opacity, and delay. And we applied it by generating different &lt;code class=&quot;language-text&quot;&gt;keyframe&lt;/code&gt; animation.&lt;/p&gt;
&lt;p&gt;The code looks pretty short, but the CSS compiled version could be huge. 😂&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;@function&lt;/span&gt; random_&lt;span class=&quot;token function&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$rand&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random_range&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$min&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$rand&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$max&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;@return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$random_range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.snow &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #a3b1bc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;@for&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; 1 &lt;span class=&quot;token keyword&quot;&gt;through&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random-x&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1000000&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; 0.0001vw&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random-offset&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; random_&lt;span class=&quot;token function&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-100000&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100000&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; 0.0001vw&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random-x-end&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$random-x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$random-offset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random-x-end-yoyo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$random-x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-offset&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random-yoyo-time&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; random_&lt;span class=&quot;token function&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;30000&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 80000&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; 100000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random-yoyo-y&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$random-yoyo-time&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; 100vh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$random-scale&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;10000&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; 0.0001&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$fall-duration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; random_&lt;span class=&quot;token function&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 30&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; 1s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$fall-delay&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;30&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; -1s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &amp;amp;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nth-child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;#{$i}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;8000&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; 0.0001&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; -10px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fall-&lt;span class=&quot;token variable&quot;&gt;#{$i}&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$fall-duration&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$fall-delay&lt;/span&gt; linear infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; fall-&lt;span class=&quot;token variable&quot;&gt;#{$i}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      #&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;percentage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-yoyo-time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-x-end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$random-yoyo-y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token selector&quot;&gt;to &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-x-end-yoyo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100vh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$random-scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And last, the snow template. It is just a bunch of &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div class=&quot;snow&quot;&gt;&lt;/code&gt;, the exact number of &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; should be equal to the &lt;code class=&quot;language-text&quot;&gt;$total&lt;/code&gt; variable we defined on the styling.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;snow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;❅&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;snow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;❅&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;snow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;❅&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;snow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;❅&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;snow&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;❅&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- code remove for brevity --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And now, you can apply the snow component into your component. That’s all. See the result below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/42699625f641839635877a971fe845e3/01.gif&quot; alt=&quot;Build a component to display snow ❄️ effect with Angular&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;set-overflow-hidden-for-body&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#set-overflow-hidden-for-body&quot; aria-label=&quot;set overflow hidden for body permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Set overflow hidden for body&lt;/h2&gt;
&lt;p&gt;Notice on the above result, there was both vertical and horizontal scrollbar. We don’t want that to happens.&lt;/p&gt;
&lt;p&gt;To fix, add &lt;code class=&quot;language-text&quot;&gt;overflow: hidden&lt;/code&gt; for &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; to style.css.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We’re done! See the final source code and output below.&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-ivy-snow?embed=1&amp;file=src/app/snow/snow.component.scss&amp;hideExplorer=1&amp;view=preview&quot;&gt;&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[Disable a reactive form control using custom directive]]></title><description><![CDATA[We will write a custom directive to disable a reactive form control by passing a boolean flag on the template]]></description><link>https://trungvose.comdisable-a-reactive-form-control-using-custom-directive/</link><guid isPermaLink="false">https://trungvose.comdisable-a-reactive-form-control-using-custom-directive/</guid><pubDate>Sat, 12 Dec 2020 16:15:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;reactive-forms-warning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reactive-forms-warning&quot; aria-label=&quot;reactive forms warning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reactive Forms warning&lt;/h2&gt;
&lt;p&gt;When working with Reactive Forms, you might probably have seen the below warning.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;It looks like you&apos;re using the disabled attribute with a reactive form directive. If you set disabled to true
when you set up this control in your component class, the disabled attribute will actually be set in the DOM for
you. We recommend using this approach to avoid &apos;changed after checked&apos; errors.

Example:
form = new FormGroup({
first: new FormControl({value: &apos;Nancy&apos;, disabled: true}, Validators.required),
last: new FormControl(&apos;Drew&apos;, Validators.required)
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When using reactive forms control, Angular want you only to interact with form control via its instance on the component class, not on the template side. To trigger this warning, you need to set a &lt;code class=&quot;language-text&quot;&gt;disabled&lt;/code&gt; input property on a reactive form control. If you familiar with Template driven form, that’s how we disable a control. Angular is complaining because we are mixing between Reactive Form and Template driven form.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReactiveFormWarningComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  disabledName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  form&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormGroup

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; fb&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;(click)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;disabledName = !disabledName&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Toggle name state&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[formGroup]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;formControlName&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[disabled]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;disabledName&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/7618eedf3105604a644c12514750a3c7/01.gif&quot; alt=&quot;DisabledControlDirective to disable Reactive Form control&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Obviously, the input will not be disabled and the UI won’t update accordingly to the boolean flag that you passed into it.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;There are two approaches to handle the above warning.&lt;/p&gt;
&lt;h3 id=&quot;1-setup-the-formcontrol-with-disabled-state&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-setup-the-formcontrol-with-disabled-state&quot; aria-label=&quot;1 setup the formcontrol with disabled state permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Setup the formControl with disabled state&lt;/h3&gt;
&lt;p&gt;We followed the warning error message and set the &lt;code class=&quot;language-text&quot;&gt;disabled&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; disabled&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-use-formcontrolenable-or-formcontroldisable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-use-formcontrolenable-or-formcontroldisable&quot; aria-label=&quot;2 use formcontrolenable or formcontroldisable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Use FormControl.enable() or FormControl.disable()&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;disable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The two approaches above will work pretty well.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But if you want to write something as below, to &lt;strong&gt;disable state of a FormControl on template&lt;/strong&gt;, we need to find another way around that.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;formControlName&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[disabled]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;disabledName&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;create-a-custom-directive&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#create-a-custom-directive&quot; aria-label=&quot;create a custom directive permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Create a custom directive&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the custom directive approach is more declarative. You declare how a control will be disabled (by a flag on the template). By calling &lt;code class=&quot;language-text&quot;&gt;disable()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;enable()&lt;/code&gt; based on the value of the flag, it’s imperative because you have to track the changes of the flag and call the methods.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So how to handle that? The answer is to write a &lt;strong&gt;Directive&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I will write a &lt;strong&gt;DisabledControlDirective&lt;/strong&gt;. This directive is applying in combination with &lt;code class=&quot;language-text&quot;&gt;formControlName&lt;/code&gt; directive or &lt;code class=&quot;language-text&quot;&gt;formControl&lt;/code&gt; directive. When you want to disable &lt;code class=&quot;language-text&quot;&gt;FormControl&lt;/code&gt; on the template, you use &lt;code class=&quot;language-text&quot;&gt;[disableControl]&lt;/code&gt; instead of the built-in &lt;code class=&quot;language-text&quot;&gt;[disabled]&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Directive&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Input &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/core&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; NgControl &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/forms&apos;&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Directive&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;([formControlName], [formControl])[disabledControl]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DisabledControlDirective&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;disabledControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;disable&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;enable&apos;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ngControl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;control&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; ngControl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NgControl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s how it looks on the template side.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[formGroup]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;formControlName&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[disabledControl]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;disabledName&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s the end result&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/bf4f699863db720a3e9d294d83e7c537/02.gif&quot; alt=&quot;DisabledControlDirective to disable Reactive Form control&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;notes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#notes&quot; aria-label=&quot;notes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Notes&lt;/h2&gt;
&lt;p&gt;If a control is disabled, its value will be excluded when accessing &lt;code class=&quot;language-text&quot;&gt;FormGroup.value&lt;/code&gt;. If you want to get all the controls value, no matter what their disabled state, please use &lt;code class=&quot;language-text&quot;&gt;FormGroup.getRawValue()&lt;/code&gt; instead.&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stackblitz.com/edit/angular-disable-reactive-form-control-directive?file=src/app/disabled-control.directive.ts&quot;&gt;https://stackblitz.com/edit/angular-disable-reactive-form-control-directive?file=src/app/disabled-control.directive.ts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks &lt;a href=&quot;https://github.com/nartc&quot;&gt;Chau Tran&lt;/a&gt; for the &lt;a href=&quot;https://github.com/angular-vietnam/100-days-of-angular/blob/master/Day043-angular-disable-control-directive.md&quot;&gt;original Vietnamese version&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 08 - Create placeholder loading (like Facebook's cards loading)]]></title><description><![CDATA[My eight tutorial is to explain how to build a placeholder loading component]]></description><link>https://trungvose.comangular-jira-clone-tutorial-08-angular-placeholder-loading/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-08-angular-placeholder-loading/</guid><pubDate>Sat, 07 Nov 2020 16:18:00 GMT</pubDate><content:encoded>&lt;p&gt;It is common to display a loading spinner to keep visitors entertained while the page is still loading. And there are two modes of a loading spinner:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;determinate&lt;/td&gt;
&lt;td&gt;Standard progress indicator fills from 0% to 100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;indeterminate&lt;/td&gt;
&lt;td&gt;Indicates that something is happening without conveying discrete progress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Spinners and progress bars are &lt;strong&gt;explicit loading paradigms&lt;/strong&gt;. They focus the user on communicating a loading period and, more often than not, are blocking user interaction until a layout has loaded enough to be useful.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No one wants to wait 🤣&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Introducing &lt;strong&gt;placeholder loading&lt;/strong&gt;, it is usually grey or neutral-toned filled shapes, meet the user instantly upon user interaction with calls to action or links. The placeholders (the so-called “bones” of the skeleton) are then replaced with the actual site content, and the illusion is complete. That’s what skeleton screens do: create the illusion of an &lt;strong&gt;instant transition&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A famous example is Facebook’ newsfeed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/max/700/0*9uxZA3XMHNjJsLT5.png&quot; alt=&quot;Angular Jira Clone Part 08 - Create placeholder loading (like Facebook&amp;#x27;s cards loading) in Angular&quot;&gt;&lt;/p&gt;
&lt;p&gt;Based on a &lt;a href=&quot;https://uxdesign.cc/what-you-should-know-about-skeleton-screens-a820c45a571a&quot;&gt;research&lt;/a&gt;, &lt;strong&gt;displaying a skeleton screen will cause humans to perceive a loading period as being shorter in duration&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;jira-clone-placeholder-loading&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jira-clone-placeholder-loading&quot; aria-label=&quot;jira clone placeholder loading permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jira clone placeholder loading&lt;/h2&gt;
&lt;p&gt;On &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt;, I also need a placeholder loading for an issue detail. I could display a spinner, but too much spinner doesn’t seem like an excellent way to interact with your user. 😂&lt;/p&gt;
&lt;p&gt;The standard-issue detail will have two columns layout.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 908px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a4085a9a7531ed626e7614ef921d3b27/a2b88/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAABYklEQVQoz6WSW2/bMAyF/f9/3dYVSdPGSpxovie2JVE3ktLgJeiKFm0f9oEPeuChDg9YdJZnBc75/B0+RK1hUcY5b8CGiEUpu8cnsd0JcZTiKJ9eDifZVudG1l0/XHPORKQ0cErMKSLGiIiERMxctO2wF+fdvnoR5+3zcft8AOuJGYki8irm1I9TP07jde6GabzMKaWbl0Ib6PrBOUuIRJhS0lov82SMoXjlOCFi0/bzomPEEDAivi5SAFgpZVVVdd3UddP1fVmWm81GiANcy6BPMQaxF8M4I9K7FApr7awMc/osJ0SSv1sDNoYAZs3pn+3Nr59VddLg1gQ4pTWYG+sz5xQj/njYNRe49GMtm0VbZr6LIyJYv2hYtPUh3ge8AYkn5cHzR1NFztk5r5QxYM3f/991MHM3XgzYnHNKH8Rfw8yzMtrYWy0KkDinddBdnD4XE7Ey4Hy4WVsP5nXn/B/8AXMxuF0rJzzwAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 08 - Create placeholder loading (like Facebook&amp;#39;s cards loading) in Angular&quot;
        title=&quot;Angular Jira Clone Part 08 - Create placeholder loading (like Facebook&amp;#39;s cards loading) in Angular&quot;
        src=&quot;/static/a4085a9a7531ed626e7614ef921d3b27/a2b88/01.png&quot;
        srcset=&quot;/static/a4085a9a7531ed626e7614ef921d3b27/8ff5a/01.png 240w,
/static/a4085a9a7531ed626e7614ef921d3b27/e85cb/01.png 480w,
/static/a4085a9a7531ed626e7614ef921d3b27/a2b88/01.png 908w&quot;
        sizes=&quot;(max-width: 908px) 100vw, 908px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I translated the above detail layout to gray &lt;code class=&quot;language-text&quot;&gt;rect&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;circle&lt;/code&gt; SVG at the end.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/5482cf804a0e6b2fae57c5cff042c682/02.gif&quot; alt=&quot;Angular Jira Clone Part 08 - Create placeholder loading (like Facebook&amp;#x27;s cards loading) in Angular&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-to-create-a-placeholder-loading&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-create-a-placeholder-loading&quot; aria-label=&quot;how to create a placeholder loading permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to create a placeholder loading&lt;/h2&gt;
&lt;p&gt;I used &lt;a href=&quot;https://github.com/ngneat/content-loader&quot;&gt;ngneat/content-loader&lt;/a&gt; for this purpose. You need to prepare an SVG structure, and &lt;code class=&quot;language-text&quot;&gt;content-loader&lt;/code&gt; will do the rest of the work for you, including the animation.&lt;/p&gt;
&lt;h3 id=&quot;install&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#install&quot; aria-label=&quot;install permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Install&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; @ngneat/content-loader&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;usage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usage&quot; aria-label=&quot;usage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Usage&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ContentLoaderModule &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@ngneat/content-loader&apos;&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ContentLoaderModule&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And specify your placeholder template:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;content-loader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;250&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;220&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;40&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;170&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;60&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;250&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;80&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;80&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;content-loader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Warning: Safari renders the SVG in black in case your Angular application uses the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;base href=&quot;/&quot;/&gt;&lt;/code&gt; tag in the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;head/&gt;&lt;/code&gt; of your &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt;. Refer to the input property &lt;code class=&quot;language-text&quot;&gt;baseUrl&lt;/code&gt; on the &lt;a href=&quot;https://github.com/ngneat/content-loader&quot;&gt;library documentation&lt;/a&gt; to fix this issue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;what-is-svgrect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-svgrect&quot; aria-label=&quot;what is svgrect permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is svg:rect&lt;/h3&gt;
&lt;p&gt;There are several basic shapes used for most SVG drawing. SVG has some predefined shape elements that can be used by developers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect&quot;&gt;Rectangle&lt;/a&gt; &lt;code class=&quot;language-text&quot;&gt;&amp;lt;rect&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle&quot;&gt;Circle&lt;/a&gt; &lt;code class=&quot;language-text&quot;&gt;&amp;lt;circle&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ellipse &lt;code class=&quot;language-text&quot;&gt;&amp;lt;ellipse&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line &lt;code class=&quot;language-text&quot;&gt;&amp;lt;line&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Polyline &lt;code class=&quot;language-text&quot;&gt;&amp;lt;polyline&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Polygon &lt;code class=&quot;language-text&quot;&gt;&amp;lt;polygon&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Path &lt;code class=&quot;language-text&quot;&gt;&amp;lt;path&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For placeholder loading, the most used two are &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect&quot;&gt;rect&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle&quot;&gt;circle&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For Angular to work, you need to specify &lt;code class=&quot;language-text&quot;&gt;svg:rect&lt;/code&gt; instead of purely &lt;code class=&quot;language-text&quot;&gt;rect&lt;/code&gt;. If you use &lt;code class=&quot;language-text&quot;&gt;rect&lt;/code&gt; only, it will give you an &lt;a href=&quot;https://stackoverflow.com/q/48453514/3375906&quot;&gt;error&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;explanation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#explanation&quot; aria-label=&quot;explanation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Explanation&lt;/h3&gt;
&lt;p&gt;In detail for &lt;code class=&quot;language-text&quot;&gt;rect&lt;/code&gt;, it is a basic SVG shape that draws rectangles, defined by their &lt;code class=&quot;language-text&quot;&gt;position&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;height&lt;/code&gt;. The rectangles may have their &lt;code class=&quot;language-text&quot;&gt;corners rounded&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;80&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Explain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;left position of the rectangle&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;x=&quot;20&quot;&lt;/code&gt; places the rectangle &lt;code class=&quot;language-text&quot;&gt;20px&lt;/code&gt; from the &lt;strong&gt;left margin&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y&lt;/td&gt;
&lt;td&gt;top position of the rectangle&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;y=&quot;100&quot;&lt;/code&gt; places the rectangle &lt;code class=&quot;language-text&quot;&gt;100px&lt;/code&gt; from the &lt;strong&gt;top margin&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;width &amp;#x26; height&lt;/td&gt;
&lt;td&gt;height and the width of the rectangle&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;width=&quot;80&quot;&lt;/code&gt; mean 80px wide and &lt;code class=&quot;language-text&quot;&gt;height=&quot;10&quot;&lt;/code&gt; mean 10px tall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rx&lt;/td&gt;
&lt;td&gt;radius on the &lt;code class=&quot;language-text&quot;&gt;x-axis&lt;/code&gt; (horizontal corner)&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;rx=&quot;3&quot;&lt;/code&gt; mean 3px radius on the x-axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ry&lt;/td&gt;
&lt;td&gt;radius on the &lt;code class=&quot;language-text&quot;&gt;y-axis&lt;/code&gt; (vertical corner)&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;ry=&quot;3&quot;&lt;/code&gt; mean 3px radius on the y-axis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;how-to-create-jira-clone-placeholder-loading&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-create-jira-clone-placeholder-loading&quot; aria-label=&quot;how to create jira clone placeholder loading permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to create Jira clone placeholder loading&lt;/h2&gt;
&lt;p&gt;Pretty straight forward, based on the issue detail layout with two columns. I went ahead and wrote the following code. You’re all set!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;content-loader&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[viewBox]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;0 0 940 260&apos;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[backgroundColor]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;#f3f3f3&apos;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[foregroundColor]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;#ecebeb&apos;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;627&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;24&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;29&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;506&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;24&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;77&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;590&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;16&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;627&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;16&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;123&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;480&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;16&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;187&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;370&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;16&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;18&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;239&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;18&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;46&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;217&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;548&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;42&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;683&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;135&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;14&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;683&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;33&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;251&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;24&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;683&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;90&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;135&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;14&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;683&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;120&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;251&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;24&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;683&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;177&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;135&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;14&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;svg:&lt;/span&gt;rect&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;683&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;207&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rx&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ry&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;251&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;24&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;content-loader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;source-code-and-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-demo&quot; aria-label=&quot;source code and demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and demo&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-placeholder-loader-like-facebook?embed=1&amp;file=src/app/issue-loader/issue-loader.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://stackblitz.com/edit/angular-placeholder-loader-like-facebook&quot;&gt;https://stackblitz.com/edit/angular-placeholder-loader-like-facebook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/issues/issue-loader/issue-loader.component.html&quot;&gt;issue-loader.component.html&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://material.angular.io/components/progress-spinner/overview&quot;&gt;Progress Spinner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uxdesign.cc/what-you-should-know-about-skeleton-screens-a820c45a571a&quot;&gt;Everything you need to know about skeleton screens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Get the last items of an array using array.slice()]]></title><description><![CDATA[I never knew the slice method takes negative integers!]]></description><link>https://trungvose.comget-the-last-items-of-an-array-using-slice/</link><guid isPermaLink="false">https://trungvose.comget-the-last-items-of-an-array-using-slice/</guid><pubDate>Tue, 03 Nov 2020 16:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;usage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usage&quot; aria-label=&quot;usage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Usage&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;slice()&lt;/code&gt; method returns a shallow copy of a portion of an array into a new array object selected from &lt;code class=&quot;language-text&quot;&gt;start&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;end&lt;/code&gt; (&lt;strong&gt;end not included&lt;/strong&gt;) where &lt;code class=&quot;language-text&quot;&gt;start&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;end&lt;/code&gt; represent the index of items in that array.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The original array will not be modified.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;See some of the common usage as below&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; array &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;/span&gt;
array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [0, 1]&lt;/span&gt;
array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [1, 2, 3]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;slice-negative&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slice-negative&quot; aria-label=&quot;slice negative permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Slice negative&lt;/h2&gt;
&lt;p&gt;But I never knew the slice method takes negative integers for the start and end index. It’s a great way of getting the last items of an array.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; array &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// [9]&lt;/span&gt;
array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [8]&lt;/span&gt;
array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [7, 8]&lt;/span&gt;
array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [] Because -1 &gt; -3, so it is like you do array.slice(3, 1) where start is greater than end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;see-more&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#see-more&quot; aria-label=&quot;see more permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;See more&lt;/h2&gt;
&lt;p&gt;Where else than &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice&quot;&gt;MDN&lt;/a&gt; 🤣&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 07 - Build a rich text editor]]></title><description><![CDATA[My seventh tutorial will focus on another kind of text editor - a rich text HTML editor]]></description><link>https://trungvose.comangular-jira-clone-tutorial-07-rich-text-editor/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-07-rich-text-editor/</guid><pubDate>Fri, 23 Oct 2020 16:18:00 GMT</pubDate><content:encoded>&lt;p&gt;If you notice, the current &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt; is using a rich text HTML editor. This tutorial will help you to create one using &lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That’s how a rich text editor looks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2bc0bafd4465090b35ca3fd9f0dc56ca/01.gif&quot; alt=&quot;Angular Jira Clone Part 07 - Build a rich text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code-and-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-demo&quot; aria-label=&quot;source code and demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and demo&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-markdown-editor-jira?embed=1&amp;file=src/app/markdown-editor/markdown-editor.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://stackblitz.com/edit/angular-markdown-editor-jira&quot;&gt;https://stackblitz.com/edit/angular-markdown-editor-jira&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/feature/gql/apps/jira-clone/src/app/shared/text-editor/text-editor.component.html&quot;&gt;text-editor.component.html&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;rich-editor-module&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rich-editor-module&quot; aria-label=&quot;rich editor module permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rich Editor Module&lt;/h2&gt;
&lt;p&gt;Like a markdown text editor, I will reuse a rich text editor in many places on a web application. So that I will create a brand new module, &lt;code class=&quot;language-text&quot;&gt;RichTextEditorModule&lt;/code&gt;, for that purpose. At the moment, it will have only one component, &lt;code class=&quot;language-text&quot;&gt;RichTextEditorComponent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 342px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9d35c2f19de6c81f6a6ea4adfd8acb8c/e2c15/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACTElEQVQ4y4WTW2sTURSF512EvgtNMpmZJDO5tCZzydyvmdybBonWPghVqaigYn+AT94o+OL//WSOWFpK8OFjz1nsWey9DkcyBkMa4zWu61GWU4pJSZrmLBYrZrMFHd2gVmtQb8h3aMhNFFW7h9RqNlBaBpqV4tkm/tgh8j1c2yKLI1FHx0eMLRPHGuGYQ2xzxPBogCo30JryHaTDroNiFej5DivfYE22BKszrGJLfHKOO9+J73B9TrB6jjvb4S+eYuYban33HtKDyRXt7Ue2r98yevkd69U3kvfXohYffuG/+cno4ivxu2uBd/lDML/6jfLsCw+LTxxMrziYfhZIdf0xdatEnl7gJHOcbEU03+JkS5LlDm9ygp0uiGanhNNTvGKNX27oOQl1w6Tete4gqXINXR8wtDORX+C6ZEmM746ZZClx4OM5ttAqqnxDz6Nn6CiNushNvYVUUzUCReaFKmP7AUEQkhcTfD+kLGckaUYcp6zXJ0IPw5h6XUZuKqhq6/4tV+KhNcMcByRJgu9XNSWOE0EYRpimhaKoaFoLVdVuqLTb5wqparLChLHrkaYZrucJwyiKhVk1sWXZ937ch9TSWlwsjokDjyyf/F05L4T5v0nn8wX9/gBZbv7fUNHaPIrPsL2QIsvFyqnILbmZtNPRaTYVseJemorokdodA9fO8fxIPLsgipmUU9KsIIwSPD/A6PbRWh1abX0vHb1Lt9dH6vWPuDQ6TLOCzZMds8WS9emW5XpDECeoVXNvgL6HTrdP73jIyB7jRwl/APMbqjGICaGPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 07 - Build a rich text editor&quot;
        title=&quot;Angular Jira Clone Part 07 - Build a rich text editor&quot;
        src=&quot;/static/9d35c2f19de6c81f6a6ea4adfd8acb8c/e2c15/02.png&quot;
        srcset=&quot;/static/9d35c2f19de6c81f6a6ea4adfd8acb8c/8ff5a/02.png 240w,
/static/9d35c2f19de6c81f6a6ea4adfd8acb8c/e2c15/02.png 342w&quot;
        sizes=&quot;(max-width: 342px) 100vw, 342px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There is not much code inside its module and component.&lt;/p&gt;
&lt;p&gt;rich-text-editor.component.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rich-text-editor&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./rich-text-editor.component.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  styleUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./rich-text-editor.component.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RichTextEditorComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;rich-text-editor.module.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;CommonModule&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  exports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;RichTextEditorComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  declarations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;RichTextEditorComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkdownEditorModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;No worry, we will add more code to the component. 😆&lt;/p&gt;
&lt;h2 id=&quot;ngx-quill&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ngx-quill&quot; aria-label=&quot;ngx quill permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ngx-quill&lt;/h2&gt;
&lt;p&gt;To build a rich text editor from scratch could take me the same time to make the whole Jira clone application. That’s why I am utilizing &lt;a href=&quot;https://github.com/KillerCodeMonkey/ngx-quill&quot;&gt;ngx-quill&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ngx-quill is an angular module for the &lt;a href=&quot;https://quilljs.com/&quot;&gt;Quill Rich Text Editor&lt;/a&gt; containing all components you need.&lt;/p&gt;
&lt;h3 id=&quot;installation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#installation&quot; aria-label=&quot;installation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Installation&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ngx-quill&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For projects using Angular &amp;#x3C; v5.0.0, please run.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ngx-quill@1.6.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;basic-usage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#basic-usage&quot; aria-label=&quot;basic usage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Basic Usage&lt;/h3&gt;
&lt;h4 id=&quot;1-import-quillmodule-into-your-appmodule&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-import-quillmodule-into-your-appmodule&quot; aria-label=&quot;1 import quillmodule into your appmodule permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Import &lt;code class=&quot;language-text&quot;&gt;QuillModule&lt;/code&gt; into your &lt;code class=&quot;language-text&quot;&gt;AppModule&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    QuillModule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;2-import-quillmodule-into-richtexteditormodule&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-import-quillmodule-into-richtexteditormodule&quot; aria-label=&quot;2 import quillmodule into richtexteditormodule permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Import &lt;code class=&quot;language-text&quot;&gt;QuillModule&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;RichTextEditorModule&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; CommonModule &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/common&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; NgModule &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/core&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; RichTextEditorComponent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./rich-text-editor.component&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; QuillModule &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ngx-quill&apos;&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;CommonModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; QuillModule&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  declarations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;RichTextEditorComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  exports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;RichTextEditorComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RichTextEditorModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;3-import-quill-themes-css-into-stylesscss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-import-quill-themes-css-into-stylesscss&quot; aria-label=&quot;3 import quill themes css into stylesscss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Import quill themes CSS into &lt;code class=&quot;language-text&quot;&gt;styles.scss&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~quill/dist/quill.core.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~quill/dist/quill.snow.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;build-our-customize-rich-text-editor-component&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#build-our-customize-rich-text-editor-component&quot; aria-label=&quot;build our customize rich text editor component permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Build our customize rich text editor component&lt;/h3&gt;
&lt;p&gt;I can now use &lt;quill-editor&gt;&lt;/quill-editor&gt; in the &lt;code class=&quot;language-text&quot;&gt;RichTextEditorComponent&lt;/code&gt;. I will go ahead and place that HTML in my component template. I set a class name &lt;code class=&quot;language-text&quot;&gt;content-editor&lt;/code&gt; so that I can style it later.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;quill-editor&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content-editor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[placeholder]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;quill-editor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See the result. Because quill is a compelling library, the rendered component has a textbox and most of the default toolbar button available for us.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/5700e953b07764c6b04a9aef50732cc0/03.gif&quot; alt=&quot;Angular Jira Clone Part 07 - Build a rich text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;My job now is pretty simple to customize the component with only the button that I need and some CSS styling.&lt;/p&gt;
&lt;h3 id=&quot;toolbar-configuration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#toolbar-configuration&quot; aria-label=&quot;toolbar configuration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Toolbar configuration&lt;/h3&gt;
&lt;p&gt;Below is the current configuration that I use for one toolbar row with some basic commands.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; QuillConfiguration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  toolbar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;bold&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;italic&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;underline&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;strike&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;blockquote&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;code-block&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; list&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ordered&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; list&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bullet&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; header&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; background&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;link&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;clean&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then I passed it down to &lt;code class=&quot;language-text&quot;&gt;modules&lt;/code&gt; input of the &lt;code class=&quot;language-text&quot;&gt;quill-editor&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;quill-editor&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content-editor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[placeholder]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[modules]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;quillConfiguration&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;quill-editor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s the result with lesser command.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/05e98c443f336752b853cd67d02f19c3/04.gif&quot; alt=&quot;Angular Jira Clone Part 07 - Build a rich text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Noted that by default, &lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt; will render a short textarea, and it will automatically expand to fill the height as you type. You might want to set a default &lt;code class=&quot;language-text&quot;&gt;min-height&lt;/code&gt;. I did set default &lt;code class=&quot;language-text&quot;&gt;120px&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;quill-editor&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content-editor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[placeholder]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[modules]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;quillConfiguration&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[styles]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{&apos;min-height&apos;: &apos;120px&apos;}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;quill-editor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I guess it looks right now. The leftover part is to connect it with a form :)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d8d7987f0eeb5e42592fc866f23f6290/05.gif&quot; alt=&quot;Angular Jira Clone Part 07 - Build a rich text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;connect-richtexteditorcomponent-to-a-form&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#connect-richtexteditorcomponent-to-a-form&quot; aria-label=&quot;connect richtexteditorcomponent to a form permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Connect RichTextEditorComponent to a form&lt;/h3&gt;
&lt;p&gt;ngx-quill provided support for both &lt;code class=&quot;language-text&quot;&gt;ReactiveForms&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;TemplateForm&lt;/code&gt;. I shifted only to use ReactiveForms. That’s why I will follow a similar approach as the Markdown component to take a &lt;code class=&quot;language-text&quot;&gt;FormControl&lt;/code&gt; as an &lt;code class=&quot;language-text&quot;&gt;Input&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RichTextEditorComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  quillConfiguration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; QuillConfiguration
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; control&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormControl

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;control &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;control &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FormControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;quill-editor&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[formControl]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[placeholder]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[modules]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;quillConfiguration&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[styles]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{&apos;min-height&apos;: &apos;120px&apos;}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content-editor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;quill-editor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See the result when I pair it inside a form. Work perfectly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/cdfaa1c3b90c45b5a20beb36cf33e57e/06.gif&quot; alt=&quot;Angular Jira Clone Part 07 - Build a rich text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;homework&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#homework&quot; aria-label=&quot;homework permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Homework&lt;/h3&gt;
&lt;p&gt;There is some small improvement that I leave it to you.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set a border when focusing into the rich text editor&lt;/li&gt;
&lt;li&gt;Implement &lt;a href=&quot;https://angular.io/api/forms/ControlValueAccessor&quot;&gt;ControlValueAccessor&lt;/a&gt; for the &lt;code class=&quot;language-text&quot;&gt;RichTextEditorComponent&lt;/code&gt; so that you can use both &lt;code class=&quot;language-text&quot;&gt;[ngModel]&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;formControl&lt;/code&gt; in a form :)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s all for a rich text editor with Angular. Any questions, you can leave it in the comment box below or reach me on Twitter. Thanks for stopping by!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to iterate over objects in TypeScript]]></title><description><![CDATA[Use let k: keyof T and a for-in loop to iterate objects when you know exactly what the keys will be or Object.entries to iterate over the keys and values of any object.]]></description><link>https://trungvose.comhow-to-iterate-over-objects-in-typescript/</link><guid isPermaLink="false">https://trungvose.comhow-to-iterate-over-objects-in-typescript/</guid><pubDate>Sat, 17 Oct 2020 15:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;From &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;Effective TypeScript: 62 Specific Ways to Improve Your TypeScript&lt;/a&gt; by &lt;a href=&quot;https://github.com/danvk&quot;&gt;Dan Vanderkam&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is an excellent book. Please &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;purchase&lt;/a&gt; the book to support its author!&lt;/p&gt;
&lt;p&gt;If you are the publisher and think this article should not be public, please write me an email to &lt;code class=&quot;language-text&quot;&gt;trungk18 [at] gmail [dot] com&lt;/code&gt; and I will make it private.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This code runs fine, and yet TypeScript flags an error in it. Why?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 726px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e91c0d969a3ccde8ed1e78e8a11f1e6a/f8067/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABeElEQVQoz52SSY7bMBBFdRPNpkhqpAbLsjXL6qQ3ff/T/KAqlhMDQQD34qGKpPjwC6IVJQVkViFOEoSBA8dx4Lrus76LlZgal3nH1aSIlfxL6H5PqHUIoRKEvocoDGDb9rdlLDwFIYIwgHeSsL0QQiuIkwvb/j3+u1hVWUKbFjqv0E5fOA8fkPIE3wvg+/7bWHEs4NgOfM9FmhtU3YSivcGcG2gdQyn9rP+DvpFSwaLRjvmFEOjXn+iWDfWPDWXbQkby5SJdUko9qn7po0jCOp6I53lI4gRxmnNSnaaIs4yFkVT8MSEfvfzn3kN4SLvuiv2+o+8HjOOEiZhmDMPI63XdMI4jV9q/33c+o57uCBH9EdJzuVw67PsH1nVl+mFAXTcMnRHGlGiaM6qqRtteuCeof458JCxMiXlZOek8L5yKOBIvy4rr9cbr263ndCSj9YvQISkJmx7N+AnTbSi7DW3XY9s2HpFkNCIlLArDlciyHHleMPSnfwGxgmrNAzxyKAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to iterate over objects in TypeScript&quot;
        title=&quot;How to iterate over objects in TypeScript&quot;
        src=&quot;/static/e91c0d969a3ccde8ed1e78e8a11f1e6a/f8067/01.png&quot;
        srcset=&quot;/static/e91c0d969a3ccde8ed1e78e8a11f1e6a/8ff5a/01.png 240w,
/static/e91c0d969a3ccde8ed1e78e8a11f1e6a/e85cb/01.png 480w,
/static/e91c0d969a3ccde8ed1e78e8a11f1e6a/f8067/01.png 726w&quot;
        sizes=&quot;(max-width: 726px) 100vw, 726px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Inspect the &lt;code class=&quot;language-text&quot;&gt;obj&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt; symbols give a clue:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 395px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f162eabb23d34f80411adee0de5d845c/2cb6c/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABmUlEQVQoz5WR2W7bMBBF9SGxtVgUSUmkZEuyJXlDbSeOizRA0e3/v+QUZOCgTYsgfjgYEjNzOZwbxHGMI4oiRCooTO1RuiCvG4qipMwLZCZRSpNlma+99r0l+FPQFTfrE832TLs50l9+0A1rur6nMNaLK6VuEZQoKSmN4fjlO92yp1oPtA8HxssD1lRIKW8T1LqgKC2n55/0mz310NMcPzF+PmPtLYJx7ItztzNj2T8+c3j6RrcaqDcD7f2B8XyPVvp9QZcMpxOmk4n/7rJtkVK/TFqUr7iHjLHemLeC17uLQZLMsKstxbzF9lu6yy+apma3yb1JDiEcwsc0TX3j//ATijSm6xfszo9UXcdyGNj3lmYu/Rqu7jtms5lfyxWttcflXHT5QKQRtsoZT08IlbMqJbvlAiGUF0yS2E/lJnQN7nzlZWrhz040SRKCNI0pjWZ3+cpyf6KyhvXC0i8qVCZed/NRAq1jxtGgyjl11zPLFFmasDA5uZL+1fdc/cdlpRKGwTK5c07fEUUhYRQxmYaEYfiXgx8R/A1Oi1+YdFoX9QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to iterate over objects in TypeScript&quot;
        title=&quot;How to iterate over objects in TypeScript&quot;
        src=&quot;/static/f162eabb23d34f80411adee0de5d845c/2cb6c/02.png&quot;
        srcset=&quot;/static/f162eabb23d34f80411adee0de5d845c/8ff5a/02.png 240w,
/static/f162eabb23d34f80411adee0de5d845c/2cb6c/02.png 395w&quot;
        sizes=&quot;(max-width: 395px) 100vw, 395px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 280px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/24964d9763066c63b79e8f846716bb8a/908b1/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABdklEQVQoz4WR6W6bUBCFeY2kyd2AS9jpxRiCF9w2tpMurrq8Qt//Eb4KcJIqStsfn2Y00jlzRuNprXkNay02DLGBjw0DoijC2vB5HlpsEGJNMM0edZ7v+2hj0NpgzBmtSeKYxeZAd/hJt/9B2axp707Ui4bmfk9z957u8wP9ty8kcYI2etJ6YRAymlprML4/9b4fkGQFZdPztt1SNT151VC3Hc6VuNua3DnKdokbNsRxPKWbDO9XH0kDn74PqIYT7t2JpF6Rbh64KRx5vSKuWxbff9HuWrr6grYWCGEwSqGFnIzU2I8n2yghWw4U7UBarwmSgjgryPKSdPhE0u2o3JZFtyfPMqQSGKMmA6VH9JPZWD0TWvLbD2TLHVHuMFFCnOVkacL6+JXheOKw3nPcHqiyHCHlq098ShjfCK4u3yCvL5HiGiUlUohJqK4u0NNsRp5Fekym5vrS2Bvjjx+eebnZzCed+TPJ3/AeN81Vo5X6p+h/hr8Bi2sIZuKoxlYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to iterate over objects in TypeScript&quot;
        title=&quot;How to iterate over objects in TypeScript&quot;
        src=&quot;/static/24964d9763066c63b79e8f846716bb8a/908b1/03.png&quot;
        srcset=&quot;/static/24964d9763066c63b79e8f846716bb8a/8ff5a/03.png 240w,
/static/24964d9763066c63b79e8f846716bb8a/908b1/03.png 280w&quot;
        sizes=&quot;(max-width: 280px) 100vw, 280px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The type of &lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt; is a string, but you’re trying to index into an object whose type only has three specific keys: &lt;code class=&quot;language-text&quot;&gt;&apos;one&apos;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&apos;two&apos;&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;&apos;three&apos;&lt;/code&gt;. There are strings other than these three, so this has to fail. Plugging in a narrower type declaration for &lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt; - &lt;code class=&quot;language-text&quot;&gt;let k: keyof typeof obj&lt;/code&gt; fixes the issue:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 465px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/05a295432536a0d598adc2df38d96e3f/9ff85/04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABIUlEQVQoz32Q2WrDMBRE/R0liWTJspZo8ZLFSd20FPr/n3SKDQktoX04zFwGhuFWUgik2BJiIQ9nSuk4dolTl0gxciyRU1/IuTCNA9fDwNgVlFJIKanr+heVbh12XxjOM+P8xXh553SemG+f5HFievtgOh4IYU9fMtPYk1P8u1AZi4k9LiRMyBifUDZi+9fV235CG4sQO4QQbHcCIZYiiZT1U2nljSc0mrA3aOMxaSBGTfQvbLcb5G6DlOJpyV9UrvV0w4VumrH5QDrN9O+f3OYbznqEFI8l/7GsX7S6XjOp79BtxIWAdw6XR0o5EPcR5xzW2lWNMbRti/d+5Z4thBDWvArB0ugaKXZordZnKylQSq6+aRq01g+9+5/3wlK26DcE+MwnUTzDhAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to iterate over objects in TypeScript&quot;
        title=&quot;How to iterate over objects in TypeScript&quot;
        src=&quot;/static/05a295432536a0d598adc2df38d96e3f/9ff85/04.png&quot;
        srcset=&quot;/static/05a295432536a0d598adc2df38d96e3f/8ff5a/04.png 240w,
/static/05a295432536a0d598adc2df38d96e3f/9ff85/04.png 465w&quot;
        sizes=&quot;(max-width: 465px) 100vw, 465px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To understand, let’s look at a slightly different example involving an interface and a function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;ABC&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;abc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ABC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; k &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; abc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// const k: string&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; abc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ~~~~~~ Element implicitly has an &apos;any&apos; type&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//        because type &apos;ABC&apos; has no index signature&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It’s the same error as before. And you can “fix” it using the same sort of declaration (&lt;code class=&quot;language-text&quot;&gt;let k: keyof typeof abc&lt;/code&gt;). But in this case, TypeScript is right to complain. Here’s why:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 512px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/990610e4d8accd6d6fd48b9bbb0a45f3/01e7c/05.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABYUlEQVQoz2WRWY4bMQxEfZLWRkrqdm+y3e7xLEGQZPKR+9/nBZINI4N8ECwSUrFYPOTlSpwvpOPMvg28fLxynFfSZWVZHVE7jHGYzmCMxVqLNRbT3bNzrvVqrnEo684yb8yXD077jWV/ZSg7249PLqWQoyApMMyJfopoCqRBOa6JmOVJ+iSMoozrznB+J+SR9Xzl268/vH3/jUrCWYcPDomBoL7hIP5eB/9FXSPMmvFBcd7jnEVESDEiIbS6rfhY84n/qf9bOfjqUYc1d4+MMXRdh6lhbYv6ofbrG2sfuXl6//NFoZMeFyI+CDlLUxgloKlHK1bBV9U5oSnjJSK5R4aenHpydPdhj8GHt5+fnPYz4zyzvSyctpUhKVO5ME8T09AzLoVlXSiXjbFcmbYb0+1KKSfO5YhqbDapKodSBsZJEfHN7HY563DWIqp47xGpBwhNvVb1FTfPHVLJUiQ+CP8C4MPnZzWLYFoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to iterate over objects in TypeScript&quot;
        title=&quot;How to iterate over objects in TypeScript&quot;
        src=&quot;/static/990610e4d8accd6d6fd48b9bbb0a45f3/01e7c/05.png&quot;
        srcset=&quot;/static/990610e4d8accd6d6fd48b9bbb0a45f3/8ff5a/05.png 240w,
/static/990610e4d8accd6d6fd48b9bbb0a45f3/e85cb/05.png 480w,
/static/990610e4d8accd6d6fd48b9bbb0a45f3/01e7c/05.png 512w&quot;
        sizes=&quot;(max-width: 512px) 100vw, 512px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The function &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt; can be called with any value &lt;strong&gt;assignable&lt;/strong&gt; to &lt;code class=&quot;language-text&quot;&gt;ABC&lt;/code&gt;, not just a value with &lt;code class=&quot;language-text&quot;&gt;&quot;a&quot;, &quot;b&quot; and &quot;c&quot;&lt;/code&gt; properties. It’s entirely possible that the value will have other properties, too. To allow for this, TypeScript gives &lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt; the only type it can be confident of, namely &lt;code class=&quot;language-text&quot;&gt;string&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Using the keyof declaration would have another downside here:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 481px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9cc05c2f6716e528bd2a50261472f0dd/d024a/06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAABg0lEQVQoz52T647TMBBG8yA0seNLnDhO4lzbZtstSwW8/wsd1GYBabWFhR9HI3k0n7+ZsRPXjFT9ET88UQ0zfl0IPiOXAiEkUgqklB8mKeuGdpxxU097OeFjh0jTe/K/BJ0PtOOEmyLxyzN17MheBTfEnY+KJ6XTuLKknCL1ukcbS7rbIYQg26Vk2W+nW/yLYGhLdFGhrMdoRe01PrRoayljwNmUPP8Hh7buqeYzYT4RusjydGB6uVIfF8IcqdwOpT7mbhM0lrFfmcYDvj8Q1++M64X5uCLTjDQT9/bf8ngp1tG6hqrwFIWnaXpiO1I7j1IKlefkb9hewPsXJb70dF3HLv2E1opuOdHun2nHA/1+YbicmS5nhnlm/nZlun7Ge095W2RZ4pyjKAqqqrrHxLuK2C9Uw4oLA6GN7M8v7C9fGZbj3aUtCqy1GGMwxqK13ty/cnP98ywJ3tOEFpErcqWRMkcbgzIWpTVCZIgHc3y35fr2zXK5Ff5K3ESyPw7/0VJ+ADwbWj+zZyuSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to iterate over objects in TypeScript&quot;
        title=&quot;How to iterate over objects in TypeScript&quot;
        src=&quot;/static/9cc05c2f6716e528bd2a50261472f0dd/d024a/06.png&quot;
        srcset=&quot;/static/9cc05c2f6716e528bd2a50261472f0dd/8ff5a/06.png 240w,
/static/9cc05c2f6716e528bd2a50261472f0dd/e85cb/06.png 480w,
/static/9cc05c2f6716e528bd2a50261472f0dd/d024a/06.png 481w&quot;
        sizes=&quot;(max-width: 481px) 100vw, 481px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If &lt;code class=&quot;language-text&quot;&gt;&quot;a&quot; | &quot;b&quot; | &quot;c&quot;&lt;/code&gt; is too narrow for &lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt;, then &lt;code class=&quot;language-text&quot;&gt;string | number&lt;/code&gt; is certainly too narrow for &lt;code class=&quot;language-text&quot;&gt;v&lt;/code&gt;. In the preceding example, one of the values is a Date, but it could be anything. The types here give a false sense of certainty that could lead to chaos at runtime.&lt;/p&gt;
&lt;p&gt;So what if you just want to iterate over the object’s keys and values without type error?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Object.entries lets you iterate over both simultaneously&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;ABC&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  c&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;abc&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ABC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;abc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    k &lt;span class=&quot;token comment&quot;&gt;// Type is string&lt;/span&gt;
    v &lt;span class=&quot;token comment&quot;&gt;// Type is any&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While these types may be hard to work with, they are at least honest!&lt;/p&gt;
&lt;p&gt;You should also be aware of the possibility of &lt;em&gt;prototype pollution&lt;/em&gt;. Even in the case of an object literal that you define, for-in can produce additional keys:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Please don&apos;t do this!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; k &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//Print x y z&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hopefully, this doesn’t happen in a real environment (You should never add enumerable properties to Object.prototype), but it is another reason that for-in produces string keys even for object literals.&lt;/p&gt;
&lt;p&gt;If you want to iterate over the keys and value in an object, use either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;code class=&quot;language-text&quot;&gt;keyof&lt;/code&gt; declaration (&lt;code class=&quot;language-text&quot;&gt;let k: keyof T&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Object.entries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The former is appropriate for constants or other situations where you know that the object won’t have additional keys, and you want precise types. The latter is more generally appropriate, though the key and value types are more challenging to work with.&lt;/p&gt;
&lt;h2 id=&quot;things-to-remember&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#things-to-remember&quot; aria-label=&quot;things to remember permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Things to remember&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;let k: keyof T&lt;/code&gt; and a for-in loop to iterate objects when you know exactly what the keys will be. Be aware that any objects your function receives as parameters might have additional keys.&lt;/li&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;Object.entries&lt;/code&gt; to iterate over the keys and values of any object.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[How to copy an object from the Chrome inspector console as code]]></title><description><![CDATA[Have you ever do console.log an object and wondering how can copy the object over?]]></description><link>https://trungvose.comhow-to-copy-an-object-from-the-chrome-inspector-console-as-code/</link><guid isPermaLink="false">https://trungvose.comhow-to-copy-an-object-from-the-chrome-inspector-console-as-code/</guid><pubDate>Sat, 10 Oct 2020 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Have you ever do &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; an object and wondering how can copy the object over? You have a few options.&lt;/p&gt;
&lt;h2 id=&quot;solution-1-copy-and-you-are-all-set&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-1-copy-and-you-are-all-set&quot; aria-label=&quot;solution 1 copy and you are all set permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution 1. Copy, and you are all set!&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Right-click to the object, select &lt;code class=&quot;language-text&quot;&gt;Store as global variable&lt;/code&gt;. Usually, Chrome stored it as &lt;code class=&quot;language-text&quot;&gt;temp1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Type &lt;code class=&quot;language-text&quot;&gt;copy(temp1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;You can paste now 😆&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/4e63116833c0bba44094efbcddcf331a/01.gif&quot; alt=&quot;How to copy an object from the Chrome inspector console as code&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Note: If you’re trying to copy a recursive object, you will get &lt;code class=&quot;language-text&quot;&gt;[object Object]&lt;/code&gt; when pasting. See the solution 2.&lt;/p&gt;
&lt;h2 id=&quot;solution-2-programmatically-do-jsonstringify-and-copy-the-string-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-2-programmatically-do-jsonstringify-and-copy-the-string-&quot; aria-label=&quot;solution 2 programmatically do jsonstringify and copy the string  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution 2: Programmatically do JSON.stringify and copy the string 🤪&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Right-click to the object, select &lt;code class=&quot;language-text&quot;&gt;Store as global variable&lt;/code&gt;. Usually, Chrome stored it as &lt;code class=&quot;language-text&quot;&gt;temp1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Type &lt;code class=&quot;language-text&quot;&gt;copy(JSON.stringify(temp1))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;There you have it!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/fa38d02ed003c0721850ed3d0d34f228/02.gif&quot; alt=&quot;How to copy an object from the Chrome inspector console as code&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;bonus-how-to-copy-json-as-typescrip-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bonus-how-to-copy-json-as-typescrip-type&quot; aria-label=&quot;bonus how to copy json as typescrip type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bonus: How to copy JSON as TypeScrip type&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Install VSCode &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype&quot;&gt;Paste JSON as Code&lt;/a&gt; extension&lt;/li&gt;
&lt;li&gt;Follow either solution 1 and 2 to copy the object&lt;/li&gt;
&lt;li&gt;Open VSCode, select View -&gt; Command Pallette or press &lt;code class=&quot;language-text&quot;&gt;Command + Shift + P&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;code class=&quot;language-text&quot;&gt;Paste JSON as Types&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;You have the type now. It supports many languages such as C# or Java, not just TypeScript.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/40815a3d40b5d30a8f0a8e06aa8b116d/03.gif&quot; alt=&quot;How to copy an object from the Chrome inspector console as code&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Use async functions instead of callbacks for asynchronous code]]></title><description><![CDATA[Prefer async/await to raw Promise when possible. They produce more concise, straightforward code and eliminate whole classes of errors.]]></description><link>https://trungvose.comuse-async-functions-instead-of-callbacks-for-asynchronous-code/</link><guid isPermaLink="false">https://trungvose.comuse-async-functions-instead-of-callbacks-for-asynchronous-code/</guid><pubDate>Tue, 06 Oct 2020 15:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;From &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;Effective TypeScript: 62 Specific Ways to Improve Your TypeScript&lt;/a&gt; by &lt;a href=&quot;https://github.com/danvk&quot;&gt;Dan Vanderkam&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is a really good book. Please &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;purchase&lt;/a&gt; the book to support its author!&lt;/p&gt;
&lt;p&gt;If you are the publisher and think this article should not be public, please write me an email to &lt;code class=&quot;language-text&quot;&gt;trungk18 [at] gmail [dot] com&lt;/code&gt; and I will make it private.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Classic JavaScript modeled asynchronous behavior using callbacks. This leads to the infamous “pyramid of doom”:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// END&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;fetchURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;fetchURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Logs:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 4&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 3&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see from the logs, the execution order is the opposite of the code iorder. This makes callback code hard to read. It gets even more confusing if you want to run the requests in parallel or bail when an error occurs.
ES2015 introduced the concept of a Promise to break the pyramid of doom. A Promise represents something that will be available in the future (they’re also sometimes called “futures”). Here’s the same code using Promises:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; page1Promise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
page1Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response1 &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response2 &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response3 &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now there’s less nesting, and the execution order more directly matches the code order. It’s also easier to consolidate error handling and use higher-order tools like &lt;code class=&quot;language-text&quot;&gt;Promise.all&lt;/code&gt;. ES2017 introduced the async and await keywords to make things even simpler:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchPages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt; keyword pauses execution of the &lt;code class=&quot;language-text&quot;&gt;fetchPages&lt;/code&gt; function until each Promise resolves. Within an async function, &lt;code class=&quot;language-text&quot;&gt;awaiting&lt;/code&gt; a Promise that throws an exception. This lets you use the usual try/catch machinery.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchPages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you target ES5 or earlier, the TypeScript compiler will perform some elaborate transformations to make async and await work. In other words, whatever your runtime, with TypeScript you can use &lt;code class=&quot;language-text&quot;&gt;async/await&lt;/code&gt;. There are a few good reasons to prefer Promises or async/await to callbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Promises are easier to compose than callbacks.&lt;/li&gt;
&lt;li&gt;Types are able to flow through Promises more easily than callbacks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to fetch the pages in parallel, for example, you can compose Promises with &lt;code class=&quot;language-text&quot;&gt;Promise.all&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchPages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;response1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response3&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using destructuring assignment with await is particularly nice in this context.&lt;/p&gt;
&lt;p&gt;TypeScript is able to inter the types of each of the three response variables as &lt;code class=&quot;language-text&quot;&gt;Response&lt;/code&gt;. The equivalent code to do the requests in parallel with callbacks requires more machinery and a type annotation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchPagesCB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; numDone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; responses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;response1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response3&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; responses&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; urls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url3&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  urls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      responses&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      numDone&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numDone &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; urls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Extending this to include error handling or to be as generic as &lt;code class=&quot;language-text&quot;&gt;Promise.all&lt;/code&gt; is challenging.&lt;/p&gt;
&lt;p&gt;Type inference also works well with &lt;code class=&quot;language-text&quot;&gt;Promise.race&lt;/code&gt;. which resolves when the first of its input Promises resolves. You can use this to add timeouts to Promises in a general way:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;millis&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;never&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;timeout&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; millis&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchWithTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ms&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;race&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ms&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The return type of &lt;code class=&quot;language-text&quot;&gt;fetchWithTimeout&lt;/code&gt; is inferred as &lt;code class=&quot;language-text&quot;&gt;Promise&amp;lt;Response&gt;&lt;/code&gt;, no type annotations required. It’s interesting to dig into why this works: the return type of &lt;code class=&quot;language-text&quot;&gt;Promise.race&lt;/code&gt;, in this case &lt;code class=&quot;language-text&quot;&gt;Promise&amp;lt;Response | never&gt;&lt;/code&gt;. But taking a union with never (the empty set) is a no-op, so this gets simplified to &lt;code class=&quot;language-text&quot;&gt;Promise&amp;lt;Response&gt;&lt;/code&gt;. When you work with Promise, all of TypeScript’s type inference machinery works to get you the right types.&lt;/p&gt;
&lt;p&gt;There are some times when you need to use raw Promises, notably when you are wrapping a callback API like &lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt;. But if you have a choice, you should generally prefer &lt;code class=&quot;language-text&quot;&gt;async/await&lt;/code&gt; to raw Promises for two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It typically produces more concise and straightforward code.&lt;/li&gt;
&lt;li&gt;It enforces that async functions always return Promises.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An async function always return a &lt;code class=&quot;language-text&quot;&gt;Promise&lt;/code&gt;, even if it doesn’t involve &lt;code class=&quot;language-text&quot;&gt;awaiting&lt;/code&gt; anything. TypeScript can help you build an intuition for this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// function getNumber(): Promise&amp;lt;number&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also create async arrow function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getNumber&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Type is () =&gt; Promise&amp;lt;number&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The raw Promise equivalent is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getNumber&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Type is () =&gt; Promise&amp;lt;number&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While it may seem odd to return a Promise for an immediately available value, this actually helps enforce an important rule: &lt;strong&gt;a function should either always be run synchronously or always be run asynchronously. It should never mix the two&lt;/strong&gt;. For example, what if you want to add a cache to the &lt;code class=&quot;language-text&quot;&gt;fetchURL&lt;/code&gt; function? Here’s an attempt:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Don&apos;t do this!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; _cache&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchWithCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; _cache&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_cache&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetchURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      _cache&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While this may seem like an optimization, the function is now extremely difficult for a client to use:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; requestStatus&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;success&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;fetchWithCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/user/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; profile &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    requestStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  requestStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What will the value of the &lt;code class=&quot;language-text&quot;&gt;requestStatus&lt;/code&gt; be after calling &lt;code class=&quot;language-text&quot;&gt;getUser&lt;/code&gt;? It depends entirely on whether the profile is cached. If it’s not, &lt;code class=&quot;language-text&quot;&gt;requestStatus&lt;/code&gt; will be set to “success”. If it is, it’ll get set to “success” and then set back to “loading”. Oops! Using async for both functions enforces consistent behavior:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; _cache&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchWithCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; _cache&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; _cache&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  _cache&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; requestStatus&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;success&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  requestStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; profile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchWithCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/user/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  requestStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now it’s completely transparent that &lt;code class=&quot;language-text&quot;&gt;requestStatus&lt;/code&gt; will end in “success”. It’s easy to accidentally produce half-synchronous code with callbacks or raw &lt;code class=&quot;language-text&quot;&gt;Promises&lt;/code&gt;, but difficult with &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note that if you return a Promise from an async function, it will not get wrapped in another Promise: the return type will be &lt;code class=&quot;language-text&quot;&gt;Promise&amp;lt;T&gt;&lt;/code&gt; rather than &lt;code class=&quot;language-text&quot;&gt;Promise&amp;lt;Promise&amp;lt;T&gt;&gt;&lt;/code&gt;. Again, TypeScript will help you build an intuition for this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Function getJSON(url: string): Promise&amp;lt;any&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; jsonPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Type is Promise&amp;lt;any&gt;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; jsonPromise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;things-to-remember&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#things-to-remember&quot; aria-label=&quot;things to remember permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Things to Remember&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Prefer Promises to callbacks for better composability and type flow.&lt;/li&gt;
&lt;li&gt;Prefer &lt;code class=&quot;language-text&quot;&gt;async/await&lt;/code&gt; to raw Promise when possible. They produce more concise, straightforward code and eliminate whole classes of errors.&lt;/li&gt;
&lt;li&gt;If a function returns a Promise, declare it &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[The different between type and interface in TypeScript]]></title><description><![CDATA[Should you use type or interface? For complex types, you have no choice: you need to use a type alias. But what about the simpler object types that can be represented either way? To answer this question, you should consider consistency and augmentation. Are you working in a codebase that consistently uses interface? Then stick with interface. Does it use type? Then use type.]]></description><link>https://trungvose.comdifferent-between-type-and-interface-typescript/</link><guid isPermaLink="false">https://trungvose.comdifferent-between-type-and-interface-typescript/</guid><pubDate>Sun, 27 Sep 2020 15:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;From &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;Effective TypeScript: 62 Specific Ways to Improve Your TypeScript&lt;/a&gt; by &lt;a href=&quot;https://github.com/danvk&quot;&gt;Dan Vanderkam&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is a really good book. Please &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;purchase&lt;/a&gt; the book to support its author!&lt;/p&gt;
&lt;p&gt;If you are the publisher and think this article should not be public, please write me an email to &lt;code class=&quot;language-text&quot;&gt;trungk18 [at] gmail [dot] com&lt;/code&gt; and I will make it private.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you want to define a named type in TypeScript, you have two options. You can use a &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; as show here:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TState&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  capital&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or an interface:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  capital&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You could also use a &lt;code class=&quot;language-text&quot;&gt;class&lt;/code&gt;, but that is a JavaScript runtime concept that also introduces a value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which should you use, type or interface? The line between these two options has become increasingly blurred over the years, to the point that in many situations you can use either. You should be aware of the distinctions that remain between type and interface and be consistent about which you use in which situation. But you should also know how to write the same types using both, so that you’ll be comfortable reading TypeScript that uses either.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Warning: The examples in this item prefix type names with I or T solely to indicate how they were defined. You should not do this in your code! Prefixing interface types with I is common in C#, and this convention made some inroads in the early days of TypeScript. But it is considered bad style today because it’s unnecessary, adds little value, and is not consistently followed in the standard libraries.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;first-the-similarities&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#first-the-similarities&quot; aria-label=&quot;first the similarities permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;First, the similarities&lt;/h2&gt;
&lt;h3 id=&quot;the-state-types-are-nearly-indistinguishable-from-one-another&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-state-types-are-nearly-indistinguishable-from-one-another&quot; aria-label=&quot;the state types are nearly indistinguishable from one another permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The State types are &lt;strong&gt;nearly indistinguishable&lt;/strong&gt; from one another&lt;/h3&gt;
&lt;p&gt;If you define an &lt;code class=&quot;language-text&quot;&gt;IState&lt;/code&gt; or a &lt;code class=&quot;language-text&quot;&gt;TState&lt;/code&gt; value with an extra property, the errors you get are character-by-character identical:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; wyoming&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Wyoming&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  capital&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Cheyenne&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  population&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500_000&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ~~~~~~~~~~~~~~~~~~ Type ... is not assignable to type &apos;TState&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//                    Object literal may only specify known properties, and&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//                    &apos;population&apos; does not exist in type &apos;TState&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;you-can-use-an-index-signature-with-both-interface-and-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#you-can-use-an-index-signature-with-both-interface-and-type&quot; aria-label=&quot;you can use an index signature with both interface and type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;You can use an index signature with both interface and type&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TDict&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IDict&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;you-can-also-define-function-types-with-either&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#you-can-also-define-function-types-with-either&quot; aria-label=&quot;you can also define function types with either permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;You can also define function types with either&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IFn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; toStrT&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;TFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; toStrI&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;IFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The type alias looks more natural for this straightforward function type, but if the type has properties as well, then the declarations start to look more alike:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TFnWithProperties&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  prop&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IFnWithProperties&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  prop&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can remember this syntax by reminding yourself that in JavaScript, functions are &lt;strong&gt;callable objects&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;both-type-aliases-and-interfaces-can-be-generic&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#both-type-aliases-and-interfaces-can-be-generic&quot; aria-label=&quot;both type aliases and interfaces can be generic permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Both type aliases and interfaces can be generic&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TPair&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  first&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  second&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IPair&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  first&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  second&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;an-interface-can-extend-a-type-with-some-caveats-explained-momentarily-and-a-type-can-extend-an-interface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#an-interface-can-extend-a-type-with-some-caveats-explained-momentarily-and-a-type-can-extend-an-interface&quot; aria-label=&quot;an interface can extend a type with some caveats explained momentarily and a type can extend an interface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;An interface can extend a type (with some caveats, explained momentarily), and a type can extend an interface&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IStateWithPop&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  population&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TStateWithPop&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; IState &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; population&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Again, these types are identical. The caveat is that an interface cannot extend a complex type like a &lt;strong&gt;union type&lt;/strong&gt;. If you want to do that, you’ll need to use &lt;code class=&quot;language-text&quot;&gt;type and &amp;amp;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;a-class-can-implement-either-an-interface-or-a-simple-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-class-can-implement-either-an-interface-or-a-simple-type&quot; aria-label=&quot;a class can implement either an interface or a simple type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A class can implement either an interface or a simple type&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  capital&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateI&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  capital&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;those-are-the-similarities-what-about-the-differences&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#those-are-the-similarities-what-about-the-differences&quot; aria-label=&quot;those are the similarities what about the differences permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Those are the similarities. What about the differences?&lt;/h2&gt;
&lt;h3 id=&quot;youve-seen-one-alreadythere-are-union-types-but-no-union-interfaces&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#youve-seen-one-alreadythere-are-union-types-but-no-union-interfaces&quot; aria-label=&quot;youve seen one alreadythere are union types but no union interfaces permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;You’ve seen one already—there are union types but no union interfaces&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AorB&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Extending union types can be useful. If you have separate types for &lt;code class=&quot;language-text&quot;&gt;Input&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Output&lt;/code&gt; variables and a mapping from name to variable:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Input&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Output&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VariableMap&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Input &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Output&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;then you might want a type that attaches the name to the variable. This would be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NamedVariable&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Input &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Output&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This type cannot be expressed with interface.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A type is, in general, more capable than an interface. It can be a union, and it can also take advantage of more advanced features like mapped or conditional types.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;type-can-also-more-easily-express-tuple-and-array-types&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#type-can-also-more-easily-express-tuple-and-array-types&quot; aria-label=&quot;type can also more easily express tuple and array types permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Type can also more easily express tuple and array types&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pair&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StringList&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NamedNums&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You &lt;strong&gt;can&lt;/strong&gt; express something like a tuple using &lt;strong&gt;interface&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tuple&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  length&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; t&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Tuple &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But this is awkward and drops all the tuple methods like concat. Better to use a type.&lt;/p&gt;
&lt;h2 id=&quot;an-interface-does-have-some-abilities-that-a-type-doesnt-however&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#an-interface-does-have-some-abilities-that-a-type-doesnt-however&quot; aria-label=&quot;an interface does have some abilities that a type doesnt however permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;An interface does have some abilities that a type doesn’t, however&lt;/h2&gt;
&lt;p&gt;One of these is that an interface can be augmented. Going back to the &lt;code class=&quot;language-text&quot;&gt;State&lt;/code&gt; example, you could have added a population field in another way:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  capital&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  population&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; wyoming&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Wyoming&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  capital&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Cheyenne&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  population&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500_000&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is known as &lt;strong&gt;“declaration merging”&lt;/strong&gt;, and it’s quite surprising if you’ve never seen it before. This is primarily used with type declaration files (Chapter 6), and if you’re writing one, you should follow the norms and use interface to support it. The idea is that there may be gaps in your type declarations that users need to fill, and this is how they do it.&lt;/p&gt;
&lt;p&gt;TypeScript uses merging to get different types for the different versions of JavaScript’s standard library. The Array interface, for example, is defined in &lt;code class=&quot;language-text&quot;&gt;lib.es5.d.ts&lt;/code&gt;. By default this is all you get. But if you add ES2015 to the lib entry of your &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt;, TypeScript will also include &lt;code class=&quot;language-text&quot;&gt;lib.es2015.d.ts&lt;/code&gt;. This includes another Array interface with additional methods like find that were added in ES2015. They get added to the other Array interface via merging. The net effect is that you get a single Array type with exactly the right methods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Merging is supported in regular code as well as declarations, and you should be aware of the possibility. If it’s essential that no one ever augment your type, then use type.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;should-you-use-type-or-interface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#should-you-use-type-or-interface&quot; aria-label=&quot;should you use type or interface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Should you use type or interface?&lt;/h2&gt;
&lt;p&gt;For complex types, you have no choice: you need to use a type alias. But what about the simpler object types that can be represented either way? To answer this question, you should consider &lt;strong&gt;consistency and augmentation&lt;/strong&gt;. Are you working in a codebase that consistently uses interface? Then stick with interface. Does it use type? Then use type.&lt;/p&gt;
&lt;p&gt;For projects without an established style, you should think about augmentation. Are you publishing type declarations for an API? Then it might be helpful for your users to be able to be able to merge in new fields via an interface when the API changes. So use interface. But for a type that’s used internally in your project, declaration merging is likely to be a mistake. So prefer type.&lt;/p&gt;
&lt;h2 id=&quot;things-to-remember&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#things-to-remember&quot; aria-label=&quot;things to remember permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Things to remember&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Understand the differences and similarities between type and interface.&lt;/li&gt;
&lt;li&gt;Know how to write the same types using either syntax.&lt;/li&gt;
&lt;li&gt;In deciding which to use in your project, consider the established style and whether augmentation might be beneficial.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 06 - Build a markdown text editor]]></title><description><![CDATA[My sixth tutorial will focus on another interesting feature - a markdown text editor]]></description><link>https://trungvose.comangular-jira-clone-tutorial-06-angular-markdown-text-editor/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-06-angular-markdown-text-editor/</guid><pubDate>Sat, 19 Sep 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you notice, the current &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt; is using an HTML text editor. I am working on making a Markdown text editor for phase two, and I think it is a good start to share with you. Part seven will guide you through an HTML text editor using &lt;code class=&quot;language-text&quot;&gt;ngx-quill&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That’s how a Markdown text editor looks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/56ed28bfdb2845fe03f5b2c5703d88ed/01.gif&quot; alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code-and-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-demo&quot; aria-label=&quot;source code and demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and demo&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-markdown-editor-jira?embed=1&amp;file=src/app/markdown-editor/markdown-editor.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://stackblitz.com/edit/angular-markdown-editor-jira&quot;&gt;https://stackblitz.com/edit/angular-markdown-editor-jira&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/feature/gql/apps/jira-clone/src/app/shared/text-editor/text-editor.component.html&quot;&gt;text-editor.component.html&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;markdown-editor-module&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#markdown-editor-module&quot; aria-label=&quot;markdown editor module permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Markdown Editor Module&lt;/h2&gt;
&lt;p&gt;A markdown text editor might be reused in many places on a web application, so that I will create a brand new module &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorModule&lt;/code&gt; for that purpose. At the moment, it will have only one component, &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorComponent&lt;/code&gt;, and it will be exported as well.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 463px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/832f6aa73b5085bce1eb842cf2dd9652/71ce0/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABfklEQVQoz43RzWvUQBzG8dzFiicvdnGTycsm2d1us9tkMi+ZTdZUCyquIlYKVopHby2C//xXNmxBrFYPH2Zgnt/DD8ZL8jlxYVDa4pyjVprGrcnznDCMiOJkEEbxvW4zXpJlTCuLans2p6c0bUf7vKdpW6xrcd0G1244kTVxmjPJpyQ72d7+PlsUzBfHeGL8DFUe4bTEWY2RJc4oTF1hakmjFU7VGFkRBz6RPyYK7tq9hf4Yb5SXjMoz5t0WffaO1eY19YstZf+Gsn+LevmeZfeKoLA8zStGU8nhtLprJjnMlngP3BWxvaDYfmP5+QeL8xuWF9+Zn99w9PGa40/XPOm/8tBe8sh94aD5k0sO1lc8rj/gxVFEoSxGK4xSWGOwWlOuVgjfH4RBQBQEw/k3kQiGrJdMJti1QWuDNhZjHcZYtDLEcYIQ4fDb4h+GjAjxwihldtLRWIu1Det1S13XpGlGEIj/KhPil8JUCNo0pZIKWUmk/K1sv+F9bjO7mZ/Ophoq1uSEeAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot;
        title=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot;
        src=&quot;/static/832f6aa73b5085bce1eb842cf2dd9652/71ce0/02.png&quot;
        srcset=&quot;/static/832f6aa73b5085bce1eb842cf2dd9652/8ff5a/02.png 240w,
/static/832f6aa73b5085bce1eb842cf2dd9652/71ce0/02.png 463w&quot;
        sizes=&quot;(max-width: 463px) 100vw, 463px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There is not much code inside its module and component.&lt;/p&gt;
&lt;p&gt;markdown-editor.component.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;markdown-editor&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./markdown-editor.component.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  styleUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./markdown-editor.component.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkdownEditorComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;markdown-editor.module.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;CommonModule&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  exports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MarkdownEditorComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  declarations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MarkdownEditorComponent&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkdownEditorModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;No worry, we will add more code below.&lt;/p&gt;
&lt;h2 id=&quot;github-markdown-toolbar&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#github-markdown-toolbar&quot; aria-label=&quot;github markdown toolbar permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Github Markdown Toolbar&lt;/h2&gt;
&lt;h3 id=&quot;install-githubmarkdown-toolbar-element-and-use-it-inside-our-angular-component&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#install-githubmarkdown-toolbar-element-and-use-it-inside-our-angular-component&quot; aria-label=&quot;install githubmarkdown toolbar element and use it inside our angular component permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Install @github/markdown-toolbar-element and use it inside our Angular component&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt; suggested me to use that package to enable a markdown toolbar. I had a look and really like that tiny package, plus It came from Github itself. 😊&lt;/p&gt;
&lt;p&gt;To add that to an Angular application, simply run.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--save&lt;/span&gt; @github/markdown-toolbar-element&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Second, you need to import &lt;code class=&quot;language-text&quot;&gt;@github/markdown-toolbar-element&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorComponent&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@github/markdown-toolbar-element&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you can paste the below code into &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorComponent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;markdown-editor.component.html&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;markdown-toolbar&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;textarea_id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;bold&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;header&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-italic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;italic&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-italic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-quote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;quote&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-quote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;code&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;link&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;image&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-unordered-list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;unordered-list&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-unordered-list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-ordered-list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;ordered-list&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-ordered-list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-task-list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;task-list&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-task-list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-mention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;mention&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-mention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;ref&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;markdown-toolbar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;textarea&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;textarea_id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;textarea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Because &lt;code class=&quot;language-text&quot;&gt;markdown-toolbar&lt;/code&gt; is a custom web element tag and it looks like an Angular component selector. Angular couldn’t find the declaration elsewhere. That’s why you see that error.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 582px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dab2df13f04368f1abe0416715d6a736/7c1cd/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA8UlEQVQoz21RW47EIAzr/Q9YtQwtlD540yN45cwwu5X2w8IxxHHEUM4T+ThQvEd2DvH1QtIaUSmkdUUyRk7qUetvLW/n+V0vi3i0+8ZQQkD1Hq01tFrRSnmC2l+981pRc37U9Bi89zDWYts2gXMOPgTs+47jOLA5J9q6rriuC7mUN3JG+ZyCUlBrxcBHbKDZsiyw1kIpJc0hBPT78BlCkJ/nCYbhPXmM8dewp9NaPwzZwJQ0oU7OZtY04Io06ZCVeUmDeZ4xTZOYjuMoZzdg8l4bYyRxT0aNw8glIffnNK5BnlISTshHtYb7vgWdd/0//ACXmgWNUn1P1gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot;
        title=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot;
        src=&quot;/static/dab2df13f04368f1abe0416715d6a736/7c1cd/03.png&quot;
        srcset=&quot;/static/dab2df13f04368f1abe0416715d6a736/8ff5a/03.png 240w,
/static/dab2df13f04368f1abe0416715d6a736/e85cb/03.png 480w,
/static/dab2df13f04368f1abe0416715d6a736/7c1cd/03.png 582w&quot;
        sizes=&quot;(max-width: 582px) 100vw, 582px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To fix it, follow the error on the screen to add &lt;code class=&quot;language-text&quot;&gt;CUSTOM_ELEMENTS_SCHEMA&lt;/code&gt; into the &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorModule&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
  schemas&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CUSTOM_ELEMENTS_SCHEMA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, something is rendering on the UI, and the textarea gets updated upon selection on the toolbar, but it hasn’t looked good just yet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/71ce5c30b00a88c7d48f0776db070817/04.gif&quot; alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;styling-the-markdown-toolbar&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#styling-the-markdown-toolbar&quot; aria-label=&quot;styling the markdown toolbar permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Styling the markdown toolbar&lt;/h3&gt;
&lt;p&gt;To make styling easier, I set a button with class &lt;code class=&quot;language-text&quot;&gt;.btn&lt;/code&gt; and wrap the text into a &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt;. I also use &lt;a href=&quot;https://icons.getbootstrap.com/&quot;&gt;Boostrap Icon&lt;/a&gt; to make it look like a real toolbar. &lt;code class=&quot;language-text&quot;&gt;markdown-editor.component.html&lt;/code&gt; is getting pretty long because all of the icons are SVG. I won’t paste all of them here. Take a look at one bold icon, and you will understand.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;markdown-toolbar&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;textarea_id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1em&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1em&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;viewBox&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0 0 16 16&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bi bi-type-bold&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;currentColor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/2000/svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;path&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;M8.21 13c2.106 0 3.412-1.087 3.412-2.823 0-1.306-.984-2.283-2.324-2.386v-.055a2.176 2.176 0 0 0 1.852-2.14c0-1.51-1.162-2.46-3.014-2.46H3.843V13H8.21zM5.908 4.674h1.696c.963 0 1.517.451 1.517 1.244 0 .834-.629 1.32-1.73 1.32H5.908V4.673zm0 6.788V8.598h1.73c1.217 0 1.88.492 1.88 1.415 0 .943-.643 1.449-1.832 1.449H5.907z&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- code removed for brevity --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;markdown-toolbar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;$&lt;span class=&quot;token property&quot;&gt;hover-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #06c&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;markdown-toolbar&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 8px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.btn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 24px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3px 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 28px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #222&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $hover-color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After styling the textarea as below, you will see quite a satisfying result. 😊&lt;/p&gt;
&lt;h3 id=&quot;styling-the-textarea&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#styling-the-textarea&quot; aria-label=&quot;styling the textarea permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Styling the textarea&lt;/h3&gt;
&lt;p&gt;I will do the styling for the textarea as well.&lt;/p&gt;
&lt;p&gt;First, I assign a class &lt;code class=&quot;language-text&quot;&gt;text-editor&lt;/code&gt; to that textarea.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;textarea&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;textarea_id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-editor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;textarea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the CSS, I wanted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No border for textarea&lt;/li&gt;
&lt;li&gt;Have border for the container around the markdown toolbar and textarea&lt;/li&gt;
&lt;li&gt;On hovering the textarea, set a different border color for the container&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope my CSS will express itself :) But if you have any questions about CSS, let me know in the comment box below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;$&lt;span class=&quot;token property&quot;&gt;border-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #d9d9d9&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;:host&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid $border-color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 0 1px $border-color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; column&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  

  &lt;span class=&quot;token selector&quot;&gt;.text-editor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 15px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 15px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;resize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;overflow-y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;&amp;amp;:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&amp;amp;.focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid $hover-color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 0 0 1px $hover-color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I have a result now. Looks pretty good. But the border-color didn’t change when I select the textarea.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/6061a0a54a3373dd5118fa9f821331c0/05.gif&quot; alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Why? Because we need to set an extra class to the &lt;strong&gt;parent of the textarea&lt;/strong&gt;. We need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Handle &lt;code class=&quot;language-text&quot;&gt;focus&lt;/code&gt; event of the textarea to add a class named &lt;code class=&quot;language-text&quot;&gt;.focus&lt;/code&gt; to the parent container.&lt;/li&gt;
&lt;li&gt;Also, handle &lt;code class=&quot;language-text&quot;&gt;blur&lt;/code&gt; event to remove this class from the parent container.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also added &lt;code class=&quot;language-text&quot;&gt;cdkTextareaAutosize&lt;/code&gt; from &lt;a href=&quot;https://material.angular.io/cdk/text-field/api&quot;&gt;@angular/cdk/text-field&lt;/a&gt; package to make the textarea auto-expand its height when the content is too long. By default, &lt;code class=&quot;language-text&quot;&gt;textarea&lt;/code&gt; will have a scrollbar visible and won’t auto-expand. See more on my previous tutorial - &lt;a href=&quot;/blog/angular-jira-clone-tutorial-04-editable-textbox/&quot;&gt;Build an editable textbox&lt;/a&gt;. I also set the &lt;code class=&quot;language-text&quot;&gt;cdkAutosizeMinRows&lt;/code&gt; to 6 so that it will also have a certain minimum height.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;textarea&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-editor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;(focus)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;focus()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;(blur)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;blur()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;[formControl]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;MarkdownInput&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;cdkTextareaAutosize&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;[cdkAutosizeMinRows]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;6&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;textarea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkdownEditorComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HostBinding&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;class.focus&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; isFocus&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFocus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFocus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What the &lt;code class=&quot;language-text&quot;&gt;HostBinding&lt;/code&gt; does is check if &lt;code class=&quot;language-text&quot;&gt;isFocus&lt;/code&gt; is true, then Angular will add a class name &lt;code class=&quot;language-text&quot;&gt;focus&lt;/code&gt; to the component selector. It look like &lt;code class=&quot;language-text&quot;&gt;&amp;lt;markdown-editor class=&quot;focus&lt;/code&gt;. If the value is false, then remove this class then.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/c22baf5e9c4cdbd240119e706dd91581/06.gif&quot; alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;I think we are almost there, looks excellent now. The last thing is to connect this component with a form.&lt;/p&gt;
&lt;h3 id=&quot;link-the-markdown-editor-component-to-a-form&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#link-the-markdown-editor-component-to-a-form&quot; aria-label=&quot;link the markdown editor component to a form permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Link the markdown editor component to a form&lt;/h3&gt;
&lt;p&gt;Usually, the Markdown editor will be used in a form with some additional form input, and you wanted to see its value in the form instance.&lt;/p&gt;
&lt;p&gt;To do it, simply set the &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorComponent&lt;/code&gt; to accept an input, which is a &lt;code class=&quot;language-text&quot;&gt;FormControl&lt;/code&gt; so that the control can be passed into the component from the parent component form instance.&lt;/p&gt;
&lt;p&gt;The component will initial a default &lt;code class=&quot;language-text&quot;&gt;FormControl&lt;/code&gt; if there is no input passed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkdownEditorComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; control&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormControl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;control &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;control &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FormControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And bind the control to the component HTML&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;textarea&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;textarea_id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-editor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;[formControl]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;(focus)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;focus()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;(blur)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;blur()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;cdkTextareaAutosize&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;[cdkAutosizeMinRows]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;6&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;textarea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To be able to do that, you have to import &lt;code class=&quot;language-text&quot;&gt;ReactiveFormsModule&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorModule&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NgModule&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    CommonModule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ReactiveFormsModule
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkdownEditorModule&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To test it with a form, I will create a simple form with two inputs by &lt;code class=&quot;language-text&quot;&gt;FormBuilder&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Title as the normal textbox&lt;/li&gt;
&lt;li&gt;Description as the markdown editor&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  form&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormGroup&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _fb&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_fb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, I am Trung&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Validators&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is a markdown text editor for - http://jira.trungk18.com/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;descriptionControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; FormControl
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I also get the description control from my form and then send it to the &lt;code class=&quot;language-text&quot;&gt;MarkdownEditorComponent&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[formGroup]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Title&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;formControlName&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-describedby&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Description&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;markdown-editor&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[control]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;descriptionControl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;markdown-editor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;alert alert-info&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  {{ form.value | json }}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sweet, everything seems working as expected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/c53f3b9980ade655ce95471324f6e9fd/07.gif&quot; alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;accessibility&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accessibility&quot; aria-label=&quot;accessibility permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Accessibility&lt;/h3&gt;
&lt;p&gt;Last but not least, remember to add the &lt;code class=&quot;language-text&quot;&gt;aria-label&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt; for all of the icons. Otherwise, If users are not familiar with the text edit icon, they might find it difficult to understand the meaning. The &lt;code class=&quot;language-text&quot;&gt;aria-label&lt;/code&gt; is for people with disability can have an easy navigation through your website :)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;markdown-toolbar&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;textarea_id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;md-bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Bold&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Bold&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1em&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1em&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;viewBox&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0 0 16 16&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bi bi-type-bold&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;currentColor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/2000/svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;path&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;M8.21 13c2.106 0 3.412-1.087 3.412-2.823 0-1.306-.984-2.283-2.324-2.386v-.055a2.176 2.176 0 0 0 1.852-2.14c0-1.51-1.162-2.46-3.014-2.46H3.843V13H8.21zM5.908 4.674h1.696c.963 0 1.517.451 1.517 1.244 0 .834-.629 1.32-1.73 1.32H5.908V4.673zm0 6.788V8.598h1.73c1.217 0 1.88.492 1.88 1.415 0 .943-.643 1.449-1.832 1.449H5.907z&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;md-bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- code removed for brevity --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;markdown-toolbar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now when you hover over the icon for sometimes, the browser will display the title.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/79517c74a327a86653a817bc8afdbb70/08.gif&quot; alt=&quot;Angular Jira Clone Part 06 - Build a markdown text editor&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;That’s all for building a Markdown Editor with Angular. Any questions, you can leave it in the comment box below or reach me on Twitter. Thanks for stopping by!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Apply types to entire function expressions when possible]]></title><description><![CDATA[Consider applying type annotations to entire function expressions, rather than to their parameters and return type. If you're writing the same type signature repeatedly, factor out a function type.]]></description><link>https://trungvose.comapply-types-to-entire-function-expressions-when-possible/</link><guid isPermaLink="false">https://trungvose.comapply-types-to-entire-function-expressions-when-possible/</guid><pubDate>Thu, 17 Sep 2020 16:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;From &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;Effective TypeScript: 62 Specific Ways to Improve Your TypeScript&lt;/a&gt; by &lt;a href=&quot;https://github.com/danvk&quot;&gt;Dan Vanderkam&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It is a really good book. Please &lt;a href=&quot;https://effectivetypescript.com/&quot;&gt;purchase&lt;/a&gt; the book to support its author!&lt;/p&gt;
&lt;p&gt;If you are the publisher and think this article should not be public, please write me an email to &lt;code class=&quot;language-text&quot;&gt;trungk18 [at] gmail [dot] com&lt;/code&gt; and I will make it private.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;JavaScript (JS) and TypeScript (TS) distinguishes a &lt;strong&gt;function statement&lt;/strong&gt; and a &lt;strong&gt;function expression&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rollDice1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sides&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Statement&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;rollDice2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sides&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Expression&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rollDice3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sides&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Also expression&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;An advantage of function expressions in TS is that you can apply a type declaration to the &lt;strong&gt;entire function&lt;/strong&gt; at one, rather than specifying the types of the parameters and return type individually.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DiceRollFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sides&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rollDice&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;DiceRollFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sides &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you mouse over sides in your editor, you’ll see that TypeScript knows its type is number. The function type doesn’t provide much value in such a simple example, but the technique does open up a number of possibilities.&lt;/p&gt;
&lt;h2 id=&quot;1-reducing-repetition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-reducing-repetition&quot; aria-label=&quot;1 reducing repetition permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Reducing repetition&lt;/h2&gt;
&lt;p&gt;If you wanted to write several functions for doing arithmetic on numbers, for instance, you could write them like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or consolidate the repeated functions signatures with a single function type:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BinaryFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; add&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;BinaryFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sub&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;BinaryFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mul&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;BinaryFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; div&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;BinaryFn&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This has fewer type annotations than before, and they’re separated away from the function implementations. This makes the logic more apparent. You’ve also gained a check that the return type of all the function expressions is &lt;strong&gt;number&lt;/strong&gt;. Libraries often provide types for common function signatures. For example, ReactJS provide a &lt;code class=&quot;language-text&quot;&gt;MouseEventHandler&lt;/code&gt; type that you can apply to an entire function rather than specifying &lt;code class=&quot;language-text&quot;&gt;MouseEvent&lt;/code&gt; as type for the function’s parameter. If you’re library author, consider providing type declarations for common callbacks.&lt;/p&gt;
&lt;h2 id=&quot;2-match-the-signature-of-some-other-function&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-match-the-signature-of-some-other-function&quot; aria-label=&quot;2 match the signature of some other function permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Match the signature of some other function&lt;/h2&gt;
&lt;p&gt;In a web browser, for example, the &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; function issues an HTTP request for some resource:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; responseP &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/quote?by=Mark+Twain&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Type is Promise&amp;lt;Response&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You extract data from the response via &lt;code class=&quot;language-text&quot;&gt;response.json()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;response.text()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getQuote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/quote?by=Mark+Twain&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; quote &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; quote&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// {&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   &quot;quote&quot;: &quot;If you tell the truth, you don&apos;t have to remember anything.&quot;,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   &quot;source&quot;: &quot;notebook&quot;,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   &quot;date&quot;: &quot;1894&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There’s a bug here: if the request for &lt;code class=&quot;language-text&quot;&gt;/quote&lt;/code&gt; fails, the response body is likely to contain an explanation like &lt;code class=&quot;language-text&quot;&gt;404 Not Found&lt;/code&gt;. This isn’t JSON, so &lt;code class=&quot;language-text&quot;&gt;response.json()&lt;/code&gt; will return a rejected Promise with a message about invalid JSON. This obscures the real error, which was a 404. It’s easy to forget that an error response with fetch not result in a rejected Promise. Let’s write a &lt;code class=&quot;language-text&quot;&gt;checkFetch&lt;/code&gt; function to do the status check for us. The type declarations for fetch in &lt;code class=&quot;language-text&quot;&gt;lib.dom.d.ts&lt;/code&gt; look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RequestInfo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RequestInit
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Response&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So you can write &lt;code class=&quot;language-text&quot;&gt;checkFetch&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkedFetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RequestInfo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RequestInit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Converted to a rejected Promise in an async function&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Request failed: &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This works, but can be written more concisely:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; checkedFetch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Request failed: &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We’ve changed from a function statement to a function expression and applied a type &lt;code class=&quot;language-text&quot;&gt;typeof fetch&lt;/code&gt; to the entire function. This allows TS to inter the types of the &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;init&lt;/code&gt; parameters.&lt;/p&gt;
&lt;p&gt;The type annotation also guarantees that the return type of &lt;code class=&quot;language-text&quot;&gt;checkFetch&lt;/code&gt; will be the same as that of &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt;. Had you written &lt;code class=&quot;language-text&quot;&gt;return&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;throw&lt;/code&gt;, for example, TS would have caught the mistake:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; checkedFetch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fetch&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//  ~~~~~~~~~~~~   Type &apos;Promise&amp;lt;Response | HTTPError&gt;&apos;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//                     is not assignable to type &apos;Promise&amp;lt;Response&gt;&apos;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//                   Type &apos;Response | HTTPError&apos; is not assignable&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//                       to type &apos;Response&apos;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Request failed: &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The same mistake in the first example would likely have led to an error, but in the code that called &lt;code class=&quot;language-text&quot;&gt;checkFetch&lt;/code&gt;, rather than in the implementation.&lt;/p&gt;
&lt;p&gt;In addition to being more concise, typing this entire function expression instead of its parameters has given you a better safety. When you’re writing a function that has the same type signature as another one, or writing many functions with the same type signature, consider whether you can apply a type declaration to entire functions, rather than repeating types of parameters and return values.&lt;/p&gt;
&lt;h2 id=&quot;things-to-remember&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#things-to-remember&quot; aria-label=&quot;things to remember permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Things to remember&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Consider applying type annotations to entire function expressions, rather than to their parameters and return type&lt;/li&gt;
&lt;li&gt;If you’re writing the same type signature repeatedly, factor out a function type or look for an existing one. If you’re a library author, provide types for common callbacks.&lt;/li&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;typeof fn&lt;/code&gt;  to match the signature of another function.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 05 - Build an interactive drag and drop board]]></title><description><![CDATA[My fifth tutorial will focus on one of the most interesting feature - drag and drop board]]></description><link>https://trungvose.comangular-jira-clone-tutorial-05-interactive-drag-and-drop-board/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-05-interactive-drag-and-drop-board/</guid><pubDate>Sun, 13 Sep 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My fifth tutorial for &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt; will focus on one of the most interesting features - drag and drop board.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;requirement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requirement&quot; aria-label=&quot;requirement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Requirement&lt;/h2&gt;
&lt;p&gt;That’s how a drag and drop board should look&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d368e0a9a316e38cf4a0cd7469cdb6fc/01.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There are several columns/lanes, each lane will have a list of card/issue&lt;/li&gt;
&lt;li&gt;A card can be moved within the same column&lt;/li&gt;
&lt;li&gt;A card can also be moved between different column&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To archive the drag and drop functionality, I head straight to &lt;a href=&quot;https://material.angular.io/cdk/drag-drop/overview&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;@angular/cdk/drag-drop&lt;/code&gt;&lt;/a&gt; and import its &lt;code class=&quot;language-text&quot;&gt;DragDropModule&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code-and-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-demo&quot; aria-label=&quot;source code and demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and demo&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-board-drag-and-drop?file=src%2Fapp%2Fboard-dnd-list%2Fboard-dnd-list.component.scss&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://stackblitz.com/edit/angular-board-drag-and-drop&quot;&gt;https://stackblitz.com/edit/angular-board-drag-and-drop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/board/board-dnd-list/board-dnd-list.component.ts&quot;&gt;board-dnd-list.component.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;https://jira.trungk18.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;drag-and-drop&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#drag-and-drop&quot; aria-label=&quot;drag and drop permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Drag and drop&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;@angular/cdk/drag-drop&lt;/code&gt; module provides you with a way to easily and declaratively create drag-and-drop interfaces, with support for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Free dragging&lt;/li&gt;
&lt;li&gt;Sorting within a list&lt;/li&gt;
&lt;li&gt;Transferring items between lists&lt;/li&gt;
&lt;li&gt;Animations&lt;/li&gt;
&lt;li&gt;Touch devices&lt;/li&gt;
&lt;li&gt;Custom drag handles, previews, and placeholders&lt;/li&gt;
&lt;li&gt;Horizontal lists and locking along an axis&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Based on the features that &lt;code class=&quot;language-text&quot;&gt;cdk/drag-drop&lt;/code&gt; provided, I can build the board drag and drop easily 🤣&lt;/p&gt;
&lt;h3 id=&quot;to-make-an-element-draggable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#to-make-an-element-draggable&quot; aria-label=&quot;to make an element draggable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;To make an element draggable&lt;/h3&gt;
&lt;p&gt;Start by importing &lt;code class=&quot;language-text&quot;&gt;DragDropModule&lt;/code&gt; into the NgModule where you want to use drag-and-drop features. You can now add the &lt;code class=&quot;language-text&quot;&gt;cdkDrag&lt;/code&gt; directive to elements to make them draggable.&lt;/p&gt;
&lt;p&gt;This is the HTML code with &lt;code class=&quot;language-text&quot;&gt;cdkDrag&lt;/code&gt; attached.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;example-box&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDrag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Simple div - Drag me around
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You need some simple CSS too. Usually, you will need to set the &lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt; property.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.example-box&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  //code removed for brevity
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; box-shadow 200ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 3px 1px -2px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0 2px 2px 0 &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.14&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    0 1px 5px 0 &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.12&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.example-box:active&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 5px 5px -3px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0 8px 10px 1px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.14&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    0 3px 14px 2px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.12&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And voila, that’s the result.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/1efb4355287063efadc06f2680775372/02.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;prepare-the-layout-for-drag-and-drop-between-multiple-columns&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prepare-the-layout-for-drag-and-drop-between-multiple-columns&quot; aria-label=&quot;prepare the layout for drag and drop between multiple columns permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Prepare the layout for drag and drop between multiple columns&lt;/h3&gt;
&lt;p&gt;You can add &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; elements to constrain where elements may be dropped. When outside of a &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; element, draggable elements can be freely moved around the page as the above example.&lt;/p&gt;
&lt;p&gt;First, let look into my data model how I represent the lane view. For each lane, there will be a list of issues. For example “Backlog” lane has two issues, “In progress” lane has three issues.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JLane&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IssueStatus
  title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  issues&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JIssue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JIssue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IssueStatus
  type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IssueType
  priority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IssuePriority
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the sake of this blog post simplicity, each issue will only contain some basic information. The most important property of an issue is the id and status. You might notice the issue status is for grouping them into a different lane.&lt;/p&gt;
&lt;p&gt;I have this structure of components in mind&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BoardDndComponent: the parent component that take a list of lane and render them
&lt;ul&gt;
&lt;li&gt;BoardDnDListComponent: to render a lane with a list of issue
&lt;ul&gt;
&lt;li&gt;IssueCardComponent: to represent a single issue&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;1-issuecardcomponent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-issuecardcomponent&quot; aria-label=&quot;1 issuecardcomponent permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. IssueCardComponent&lt;/h3&gt;
&lt;p&gt;This component is simple, only take an &lt;code class=&quot;language-text&quot;&gt;issue&lt;/code&gt; which is in type &lt;code class=&quot;language-text&quot;&gt;JIssue&lt;/code&gt; and render it to the UI. For now, only the issue title will be displayed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IssueCardComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; issue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JIssue
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;issue-wrap&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;issue&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;pb-3 text-15 text-textDarkest&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      {{ issue.title }}
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.issue-wrap&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;touch-action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; manipulation&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -webkit-grab&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grab&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.issue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex-grow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; column&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.125rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition-property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition-duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.1s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-boarddndlistcomponent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-boarddndlistcomponent&quot; aria-label=&quot;2 boarddndlistcomponent permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. BoardDnDListComponent&lt;/h3&gt;
&lt;p&gt;This component will take a lane as input and render a list of issues for that lane. Also, we will be using &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;cdkDrag&lt;/code&gt; on that component to enable the drag and drop capability.&lt;/p&gt;
&lt;p&gt;Take note that I set the selector surrounded by a square bracket &lt;code class=&quot;language-text&quot;&gt;[board-dnd-list]&lt;/code&gt;, which mean I will do the attribute selection for that component, to reduce one level deeper of CSS styling purpose, you will see on the &lt;code class=&quot;language-text&quot;&gt;BoardDndComponent&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[board-dnd-list]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  templateUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./board-dnd-list.component.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  styleUrls&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./board-dnd-list.component.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BoardDndListComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; lane&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JLane
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I also associated some arbitrary data with both &lt;code class=&quot;language-text&quot;&gt;cdkDrag&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; by setting &lt;code class=&quot;language-text&quot;&gt;cdkDragData&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;cdkDropListData&lt;/code&gt; to the issue and the list of issue, respectively. Events fired from both directives include this data, allowing to easily identify the origin of the drag or drop interaction.
The lane id will also be set to the &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;status-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;px-3 pb-4 pt-3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ lane.title }}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;issue-card-container pl-2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;cdkDropList&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[cdkDropListData]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lane.issues&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[id]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lane.id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;issue-card&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let issue of lane.issues&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;[issue]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;issue&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;[cdkDragData]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;issue&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;cdkDrag&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;issue-card&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-boarddndcomponent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-boarddndcomponent&quot; aria-label=&quot;3 boarddndcomponent permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. BoardDndComponent&lt;/h3&gt;
&lt;p&gt;That final part is to glue them together. Because I have an unknown number of connected drop lists, I set the &lt;code class=&quot;language-text&quot;&gt;cdkDropListGroup&lt;/code&gt; directive to set up the connection automatically. Note that any new &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; that is added under a group will be connected to all other lists automatically.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;d-flex&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDropListGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;board-dnd-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;board-dnd-list&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let lane of lanes&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[lane]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lane&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that’s the result.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d3730ee6337b073b24d619fb2732613d/03.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, I can start dragging the card. But still, need to handle the animation and after the drop event and update the data to get displayed on the UI.&lt;/p&gt;
&lt;h2 id=&quot;adding-animation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#adding-animation&quot; aria-label=&quot;adding animation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Adding animation&lt;/h2&gt;
&lt;p&gt;Follow &lt;a href=&quot;https://material.angular.io/cdk/drag-drop/overview#styling&quot;&gt;styling&lt;/a&gt; section, I will modify some of the class that was added by the directives.&lt;/p&gt;
&lt;p&gt;First, I need to style &lt;code class=&quot;language-text&quot;&gt;IssueCardComponent&lt;/code&gt; component host style to make it has a property height.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:host&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;cdk-drag-placeholder-class&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cdk-drag-placeholder-class&quot; aria-label=&quot;cdk drag placeholder class permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;cdk-drag-placeholder class&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;This is an element that will be shown instead of the real element as it’s being dragged inside a cdkDropList. By default, this will look exactly like the element that is being sorted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I need to style this class to make my element look different where it is staying while being dragged around.&lt;/p&gt;
&lt;p&gt;This is the current behavior &lt;strong&gt;before styling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/bddc16cd773f298058d8964ae2ff5674/04-no-placeholder.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;I wanted the current card to look like a place holder only with a dashed border, and the content is invisible.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.cdk-drag-placeholder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.issue-wrap&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;150&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 150&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 200&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px dashed #abc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.issue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that’s how it looks &lt;strong&gt;after styling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/ad6f22fa9701de83bfa6c0c6dfd9ce2e/05-placeholder.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;cdk-drop-list-dragging-class&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cdk-drop-list-dragging-class&quot; aria-label=&quot;cdk drop list dragging class permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;cdk-drop-list-dragging class&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;A class that is added to cdkDropList while the user is dragging an item.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I style that to have some animation in place.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.cdk-drop-list-dragging&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 250ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.cdk-drag:not(.cdk-drag-placeholder)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 250ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/5b848ffda7367341ece6640512daea68/06-animation.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;update-data-after-dropping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update-data-after-dropping&quot; aria-label=&quot;update data after dropping permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update data after dropping&lt;/h2&gt;
&lt;p&gt;So what you have seen so far is just the UI, after drag and drop and item. You need to update the data to reflect what has been changed on the UI, whether it is updating the position property or just an index of an array.&lt;/p&gt;
&lt;p&gt;You can listen to the &lt;code class=&quot;language-text&quot;&gt;cdkDropListDropped&lt;/code&gt; event on where you attached &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; directive to handle.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;issue-card-container pl-2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;cdkDropList&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[cdkDropListData]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lane.issues&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;(cdkDropListDropped)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;drop($event)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[id]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lane.id&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The function is pretty simple. If it is happening inside the same lane, you call the build-in util function of cdk to moveItemInArray. If it is moving between two lanes, call a function to update the indices between array.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Noted that those utils will modify the array in place. So if you are using any state management, you should consider copying the array to the new one before modifying.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CdkDragDrop&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;JIssue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; isMovingInsideTheSameList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousContainer &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isMovingInsideTheSameList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;moveItemInArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;transferArrayItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousContainer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentIndex
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See the result, looks good now. But do you notice something? Seems like you can’t drag the card to the end of the other list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2160c8bb435727bf9f2a11d953c7543b/07-update-data.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;To fix that, simply set the container for the list of issue-card to be full height.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.status-list&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;//code removed for brevity

  .issue-card-container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The final result is here!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/34466e62ebf4b47728cce43463c76aad/08.gif&quot; alt=&quot;Angular Jira Clone Part 05 - Build an interactive drag and drop board&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;That’s all for the fifth part. Any questions, you can leave it on the comment box below or reach me on Twitter. Thanks for stopping by!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to kill the process currently using a given port on Windows]]></title><description><![CDATA[A quick trick to kill the process currently using a port on Windows - npx kill-port 4200]]></description><link>https://trungvose.comkill-process-running-on-given-port-windows/</link><guid isPermaLink="false">https://trungvose.comkill-process-running-on-given-port-windows/</guid><pubDate>Sat, 12 Sep 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sometimes when I tried running my Angular application and saw this message.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An unhandled exception occurred: Port 4200 is already in use. Use ‘—port’ to specify a different port.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 741px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8a976ee5d5667d7500961f01dbe0dc45/13e20/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABp0lEQVQoz3VS247UMBSb32CaW3NvLm3ame6yMKwArcQLgv//GqOcKbO88GDZjhrrND4ns2bIFnGeHYZJI4qMpD1K/YBaByxtwLIMKJVjnhlKFWAhg6cZ3HoIzsCFgDhwuu47Us7wwcOHgBAipmlCShNi7N4jhAClJITgkPLvZXlnzh9hFLi0BuscxnHEqDWMtXDeE7om7xyElGCcg3MBTvwOdqDrU00ZHWVKKClDKwXBGBTnBElgkH26Y4pRCKh/2EoJfejTz1LxI1X8Whp+txVv3uOT1vhmLb5ai+/W4s05fDYGL8bgWWu8aI2bMXi1Bq/GYB1HzKOi4NNNG3z0O/ZlxaUWTMYgGgM/jvSBkxJeStLm8H0yc/h+3kt5/PIX77Ffn7Bdrogp0Tv6ECGVwnkYMDCGM2PE/wNjjMKolJtzqHPFPM9orWHdNpRS6WxZFmrYGEOwVNC9JGvfuUMpdZ+waY1aZ/S2t8uFQntQ921dScc40aXOfaVijJhSQs6FdIiRtoQC+4M65yls2zbi/Wk/+Bl9T0spFFBrRc752NNE3H2p9THhH26JH/LSNXfxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to kill the process running on given port on Windows&quot;
        title=&quot;How to kill the process running on given port on Windows&quot;
        src=&quot;/static/8a976ee5d5667d7500961f01dbe0dc45/13e20/01.png&quot;
        srcset=&quot;/static/8a976ee5d5667d7500961f01dbe0dc45/8ff5a/01.png 240w,
/static/8a976ee5d5667d7500961f01dbe0dc45/e85cb/01.png 480w,
/static/8a976ee5d5667d7500961f01dbe0dc45/13e20/01.png 741w&quot;
        sizes=&quot;(max-width: 741px) 100vw, 741px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To simple kill the process that is running on a specific port, run this command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx kill-port &lt;span class=&quot;token number&quot;&gt;4200&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where &lt;code class=&quot;language-text&quot;&gt;4200&lt;/code&gt; is the port that needs to be stopped.&lt;/p&gt;
&lt;p&gt;And the result is satisfying.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 789px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a2d9c81abff126641346fcc8ca650a7/44fd6/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABYUlEQVQoz22O25LbIBBE/RsWIAmwQSB0R9bG2ark/3/qpIQ36zzk4VR3T031zOWeB/zHjP/csM+JuwlMemReJMsqybskZ8k4KZZF4IcbIi6oOKLqmlqpl35x2eaVIz94bDsf+SANiUpVVFXN9frmbxaVpBZXalG9y5R6F7a9p02eenCo0eFsIDQdIQhiLxgGQUqCGBUpSe7BIHyP8vH/H07bSv5x4PrALXi6LtCHxDB09L2jT66oMTXWKFrdoLShPvmn6Luw85EYIm3TFLTR2JvF2hvaWLQ2BSkVQiiklCgpUFIWX7JS3/7SJ09KkWlamOalfHheatuW5jzStrRaY8xZrF8YU7K19mv+yieXxxF4PjeO/eCxP8j7zrquRad5Ju+ZEAIhRpz3OOfxXVfoU0+MsfhzpwuBy7zs/Pwcyb8d+VdHzpp910XX9aQlbw1muCNGjxo98mQ61SHT/cWZo+UPe6/mHd8SVlMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to kill the process running on given port on Windows&quot;
        title=&quot;How to kill the process running on given port on Windows&quot;
        src=&quot;/static/1a2d9c81abff126641346fcc8ca650a7/44fd6/02.png&quot;
        srcset=&quot;/static/1a2d9c81abff126641346fcc8ca650a7/8ff5a/02.png 240w,
/static/1a2d9c81abff126641346fcc8ca650a7/e85cb/02.png 480w,
/static/1a2d9c81abff126641346fcc8ca650a7/44fd6/02.png 789w&quot;
        sizes=&quot;(max-width: 789px) 100vw, 789px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You must have &lt;code class=&quot;language-text&quot;&gt;npm@5.2.0^&lt;/code&gt; version to run &lt;code class=&quot;language-text&quot;&gt;npx&lt;/code&gt;, which means as the minimum of npm running on your machine must be &lt;code class=&quot;language-text&quot;&gt;5.2.0&lt;/code&gt; or greater.&lt;/p&gt;
&lt;p&gt;You can also read more about kill-port here: &lt;a href=&quot;https://www.npmjs.com/package/kill-port&quot;&gt;https://www.npmjs.com/package/kill-port&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Use VSCode Like a PRO]]></title><description><![CDATA[VSCode is one of the most popular code editors, skilled use of VSCode can greatly improve our programming efficiency. This post consisted of some tips I found very helpful when working with VSCode, which I hope will help you too.]]></description><link>https://trungvose.comvscode-like-a-pro/</link><guid isPermaLink="false">https://trungvose.comvscode-like-a-pro/</guid><pubDate>Wed, 09 Sep 2020 01:25:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VSCode&lt;/a&gt; is one of the most popular code editors, skilled use of VSCode can greatly improve our programming efficiency. This post consisted of some tips I found very helpful when working with VSCode, which I hope will help you too.&lt;/p&gt;
&lt;h2 id=&quot;multiple-selections-multi-cursor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#multiple-selections-multi-cursor&quot; aria-label=&quot;multiple selections multi cursor permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Multiple selections (multi-cursor)&lt;/h2&gt;
&lt;p&gt;We might want to type the same thing in different places at the same time.&lt;/p&gt;
&lt;h3 id=&quot;altclick-to-add-the-secondary-cursor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#altclick-to-add-the-secondary-cursor&quot; aria-label=&quot;altclick to add the secondary cursor permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Alt+Click to add the secondary cursor&lt;/h3&gt;
&lt;p&gt;VS Code supports multiple cursors for fast simultaneous edits. You can add secondary cursors (rendered thinner) with &lt;code class=&quot;language-text&quot;&gt;Alt+Click&lt;/code&gt;. Each cursor operates independently based on the context it sits in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/bd8488c4d8af6a2f40714854191369e0/01.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;A common way to add more cursors is with &lt;code class=&quot;language-text&quot;&gt;Ctrl+Alt+Down&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Ctrl+Alt+Up&lt;/code&gt; that insert cursors below or above.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/988b9f78d412aff0b0603a324000c3f6/02.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ctrld-to-select-the-next-occurrence-of-the-current-selection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ctrld-to-select-the-next-occurrence-of-the-current-selection&quot; aria-label=&quot;ctrld to select the next occurrence of the current selection permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ctrl+D to select the next occurrence of the current selection&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Ctrl+D&lt;/code&gt; selects the word at the cursor or the next occurrence of the current selection.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Ctrl+F2&lt;/code&gt; to select &lt;strong&gt;all&lt;/strong&gt; of the next occurrence of the current selection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/702bccba59fb58a3d3847f0d8f09aa13/03.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;column-box-selection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#column-box-selection&quot; aria-label=&quot;column box selection permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Column (box) selection&lt;/h3&gt;
&lt;p&gt;Place the cursor in one corner and then hold &lt;code class=&quot;language-text&quot;&gt;Shift+Alt&lt;/code&gt; while dragging to the opposite corner:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/d87bfca9017d9d3bf25cdf6e7bcc2a5e/13.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;quick-open&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#quick-open&quot; aria-label=&quot;quick open permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Quick Open&lt;/h2&gt;
&lt;h3 id=&quot;ctrlp-to-quickly-open-files&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ctrlp-to-quickly-open-files&quot; aria-label=&quot;ctrlp to quickly open files permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ctrl+P to quickly open files&lt;/h3&gt;
&lt;p&gt;It will open a contextual menu for you with the auto-suggestion to navigate between files. Remember, do &lt;code class=&quot;language-text&quot;&gt;Ctrl+F&lt;/code&gt; and search for the file usually won’t return any result.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/76cdce7129442dd7f6612e5221847c2a/04.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;open-multiple-files-from-quick-open&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#open-multiple-files-from-quick-open&quot; aria-label=&quot;open multiple files from quick open permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Open multiple files from Quick Open&lt;/h3&gt;
&lt;p&gt;You can open multiple files from Quick Open by pressing the &lt;code class=&quot;language-text&quot;&gt;Right arrow&lt;/code&gt; key. This will open the currently selected file in the background and you can continue selecting files from Quick Open&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/dff95c2dd931c94bd79e5b4b7e1f0595/12.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ctrlshiftt-to-reopen-the-most-recent-closed-file&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ctrlshiftt-to-reopen-the-most-recent-closed-file&quot; aria-label=&quot;ctrlshiftt to reopen the most recent closed file permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ctrl+Shift+T to reopen the most recent closed file&lt;/h3&gt;
&lt;p&gt;It is similar to the Chrome shortcut to reopen a tab after you have close it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/e038a9947af1cef60f2a90e8d403f244/05.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Also, when you do &lt;code class=&quot;language-text&quot;&gt;Ctrl+P&lt;/code&gt; as above, it will show the most recently open file on top of the list.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 898px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1dce1c1c98db92309e4671572da77ad2/84cc5/06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABdUlEQVQoz12R2W7bMBBF9SWyJe47RZpUxEVSFFlumgR+6P//S+E0KIIMDjDzcgcHuE2MMYTLYz7XlHIuNZda6rxfj/049utxvR7Hr9fjuJU6p5xSzlMuMcYmhOCdp5RyyjC3Zvl4ev1z2e9uv9v13T6/6+U3TgfNNxA3NN5ovvP0JvOtQ7QxxFghhR2sDVyoYZhCffF5c+lZXpIKWfjE3MTcRIYRE8PJIIgx0ncANhprQxn1oxgrJXixruRccs5Tig8p56x19vF7MIYSghCAEHDOIQANxIRKyQUnGIO+d8rnVEIYnbtIoSBECGGEMPzk64aIUd73faOtFWPSzjOCOgC2EOdlrbVOUxqcV9pgQn9AKGNcnM7nRjAplO+FhFacYP9S4rbt87qmXManaXCeMk4o+w7jQkj1CJ860DMNmQZUnSHxJszzUupcSo3/iiAUoS959M28bdum6zqAORYaYHbCvEi3zqtzXikthMSI/Ij9p21PfwGkAGQi/OC8YwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Use VSCode Like Pro&quot;
        title=&quot;Use VSCode Like Pro&quot;
        src=&quot;/static/1dce1c1c98db92309e4671572da77ad2/84cc5/06.png&quot;
        srcset=&quot;/static/1dce1c1c98db92309e4671572da77ad2/8ff5a/06.png 240w,
/static/1dce1c1c98db92309e4671572da77ad2/e85cb/06.png 480w,
/static/1dce1c1c98db92309e4671572da77ad2/84cc5/06.png 898w&quot;
        sizes=&quot;(max-width: 898px) 100vw, 898px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;go-to-symbol&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#go-to-symbol&quot; aria-label=&quot;go to symbol permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Go to Symbol&lt;/h2&gt;
&lt;p&gt;You can navigate symbols inside a file with &lt;code class=&quot;language-text&quot;&gt;Ctrl+Shift+O&lt;/code&gt;. By typing &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; the symbols will be grouped by category. Press &lt;code class=&quot;language-text&quot;&gt;Up&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Down&lt;/code&gt; and navigate to the place you want.&lt;/p&gt;
&lt;p&gt;You could also activate that by &lt;code class=&quot;language-text&quot;&gt;Ctrl+P&lt;/code&gt; and then type &lt;code class=&quot;language-text&quot;&gt;@&lt;/code&gt;. It will behave exactly as you type &lt;code class=&quot;language-text&quot;&gt;Ctrl+Shift+O&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/dd0c69a05951b38500c447b3683f8b44/07.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;rename-symbol&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rename-symbol&quot; aria-label=&quot;rename symbol permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rename symbol&lt;/h2&gt;
&lt;p&gt;Some languages support rename symbols across files. Press &lt;code class=&quot;language-text&quot;&gt;F2&lt;/code&gt; and then type the new desired name and press &lt;code class=&quot;language-text&quot;&gt;Enter&lt;/code&gt;. All usages of the symbol will be renamed, across files.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/38dbc9578bd383fece5fe148ad4dc59b/08.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;It will update the name inside the same file as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/0c7bde42376babd97733b8bea3b13350/09.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;navigation-history&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#navigation-history&quot; aria-label=&quot;navigation history permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Navigation history&lt;/h2&gt;
&lt;p&gt;Navigate back: &lt;code class=&quot;language-text&quot;&gt;Alt+Left&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Navigate forward: &lt;code class=&quot;language-text&quot;&gt;Alt+Right&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/ce193215800039a8766435c39f5d4aad/11.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;shrinkexpand-selection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#shrinkexpand-selection&quot; aria-label=&quot;shrinkexpand selection permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Shrink/expand selection&lt;/h2&gt;
&lt;p&gt;Quickly shrink or expand the current selection. Trigger it with &lt;code class=&quot;language-text&quot;&gt;Shift+Alt+Left&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Shift+Alt+Right&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here’s an example of expanding the selection with &lt;code class=&quot;language-text&quot;&gt;Shift+Alt+Right&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/ffd464920646cce7032ca39a8c4c3ad8/10.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;move-lines-up-and-down&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#move-lines-up-and-down&quot; aria-label=&quot;move lines up and down permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Move lines up and down&lt;/h2&gt;
&lt;p&gt;Sometimes we want to move some code or text as a whole up or down. Press &lt;code class=&quot;language-text&quot;&gt;Alt+Up&lt;/code&gt; to move the text up; Press &lt;code class=&quot;language-text&quot;&gt;Alt+Down&lt;/code&gt; to move the text down.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/45dbe534578140ae4c84d4275709bdda/14.gif&quot; alt=&quot;Use VSCode Like Pro&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;some-extensions-that-i-am-using&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-extensions-that-i-am-using&quot; aria-label=&quot;some extensions that i am using permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some extensions that I am using&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Extensions&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost&quot;&gt;Import Cost&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Display import/require package size in the editor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome&quot;&gt;Debugger for Chrome&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Debug your code in VSCode while running the web page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Angular.ng-template&quot;&gt;Angular Language Service&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Editor services for Angular templates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker&quot;&gt;Code Spell Checker&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Help catch common spelling errors while keeping the number of false positives low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=SirTori.indenticator&quot;&gt;Indenticator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Highlights your current indent depth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=adrianwilczynski.csharp-to-typescript&quot;&gt;C# To TypeScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Quickly convert the class/interface from C# to TypeScript equivalent. Super helpful and save time for front end dev. Usually, we have to do it manually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets&quot;&gt;ES7 React/Redux/GraphQL/React-Native snippets&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Simple extensions for React, Redux and Graphql in JS/TS with ES7 syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=johnpapa.Angular2&quot;&gt;Angular Snippets (Version 9)&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Angular version 9 snippets by John Papa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens&quot;&gt;GitLens — Git supercharged&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Supercharge the Git capabilities built into Visual Studio Code — Visualize code authorship at a glance via Git blame annotations and code lens, seamlessly navigate and explore Git repositories, gain valuable insights via powerful comparison commands, and so much more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one&quot;&gt;Markdown All in One&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;All you need to write Markdown (keyboard shortcuts, table of contents, auto preview and more)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype&quot;&gt;Paste JSON as Code&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Copy JSON, paste as Go, TypeScript, C#, C++ and more.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=psioniq.psi-header&quot;&gt;psioniq File Header&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Configurable file header comment block and changes tracking.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=pflannery.vscode-versionlens&quot;&gt;Version Lens&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Shows the latest version for each package using code lens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=WakaTime.vscode-wakatime&quot;&gt;WakaTime&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Metrics, insights, and time tracking automatically generated from your programming activity.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;more-productivity-tips&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#more-productivity-tips&quot; aria-label=&quot;more productivity tips permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;More productivity tips&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/tips-and-tricks&quot;&gt;VSCode Tips and Tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/productivity-freak/avoiding-productivity-mousetraps-177d80fa533b&quot;&gt;Avoiding the productivity mouse-traps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://levelup.gitconnected.com/use-vscode-like-a-senior-developer-9b54054c452a&quot;&gt;Use VSCode Like a Senior Developer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 04 - Build an editable textbox]]></title><description><![CDATA[My fourth tutorial will focus on the first custom UI element - editable textbox]]></description><link>https://trungvose.comangular-jira-clone-tutorial-04-editable-textbox/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-04-editable-textbox/</guid><pubDate>Sat, 05 Sep 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My fourth tutorial for &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt; will focus on the first custom UI element - editable textbox. This time we will go deeper inside the code and do a step by step tutorial.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;requirement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requirement&quot; aria-label=&quot;requirement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Requirement&lt;/h2&gt;
&lt;p&gt;That’s how an editable textbox should look&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/0b606d1db77bd0f83d41c38e4be487ee/01.gif&quot; alt=&quot;Angular Jira Clone Part 04 - Build an editable textbox&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When displaying the text, it should look like a paragraph&lt;/li&gt;
&lt;li&gt;When hovering over the text, there should be a different background to tell the user that it is editable!&lt;/li&gt;
&lt;li&gt;Upon clicking into the background, it will be displayed as the textarea with a bold border for the user to edit&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You might think there is some kind of &lt;code class=&quot;language-text&quot;&gt;*ngIf&lt;/code&gt; to toggle between the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&gt;&lt;/code&gt; and a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&lt;/code&gt; but it is not how I did it. Notice that upon clicking to edit, there was no shifting on the UI. The trick is very simple.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is only one &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&lt;/code&gt; and on the normal rendering, I style it to have no border to look like a paragraph. But when you click into that, it is just a normal focus state of the built-in textarea&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;source-code-and-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-demo&quot; aria-label=&quot;source code and demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and demo&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-editable-textbox?view=preview&quot;&gt;&lt;/iframe&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://stackblitz.com/edit/angular-editable-textbox&quot;&gt;https://stackblitz.com/edit/angular-editable-textbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jira.trungk18.com/project/issue/6527&quot;&gt;https://jira.trungk18.com/project/issue/6527&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/issues/issue-title/issue-title.component.ts&quot;&gt;issue-title.component.ts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;editable-textbox&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#editable-textbox&quot; aria-label=&quot;editable textbox permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Editable Textbox&lt;/h2&gt;
&lt;p&gt;Let see how a normal textarea looks like. Simple write &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&amp;lt;/textarea&gt;&lt;/code&gt;, you will see it as below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/a4140e5a62cfbc2a200393005bd73b1c/02.gif&quot; alt=&quot;Angular Jira Clone Part 04 - Build an editable textbox&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&lt;/code&gt; tag defines a multi-line text input control.&lt;/li&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&lt;/code&gt; element is often used in a form, to collect user inputs like comments or reviews.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see from the normal textarea, it will display the edit cursor only when you focus on the textarea. If you click outside of that, there won’t be any focus. So I will go ahead and style the textarea without the border.&lt;/p&gt;
&lt;h3 id=&quot;1-no-border&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-no-border&quot; aria-label=&quot;1 no border permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. No border&lt;/h3&gt;
&lt;p&gt;Simply add the below CSS to remove the border and have some padding.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.textarea-inline-control&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 7px 7px 8px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.28&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/8266ac8ee07b033973a628c72935389e/03.gif&quot; alt=&quot;Angular Jira Clone Part 04 - Build an editable textbox&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now it looks very neutral, but still, there was a resizer at the bottom right. To remove it, add&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;resize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-on-hover&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-on-hover&quot; aria-label=&quot;2 on hover permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. On hover&lt;/h3&gt;
&lt;p&gt;When you hover over the textarea, you want to display different background colors. But when you focus on the textarea, you want the background to be white as normal. That’s why I do this CSS to only target the hover state, but not the focus state.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.textarea-inline-control&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&amp;amp;:hover:not(:focus)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;235&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 236&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 240&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s the result. Looks great!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/196c5b7cc154257fc4d2bacacafd29de/04.gif&quot; alt=&quot;Angular Jira Clone Part 04 - Build an editable textbox&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-on-focus&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-on-focus&quot; aria-label=&quot;3 on focus permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. On focus&lt;/h3&gt;
&lt;p&gt;On focus, what we want is to change the border color to be blue. It is pretty straight forward. I also added some more CSS for &lt;code class=&quot;language-text&quot;&gt;box-shadow&lt;/code&gt; and transition to make the animation smoother.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/9e7c414651dd8bf9c4644dc62021d13c/05.gif&quot; alt=&quot;Angular Jira Clone Part 04 - Build an editable textbox&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;It is kind of working now, but notice when you press enter to make a new line, the textarea won’t automatically expand to fit the new content. Usually, you can control the size by the resizer or you can specify it by the &lt;code class=&quot;language-text&quot;&gt;cols&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;rows&lt;/code&gt; attributes. For example&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;textarea&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;4&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cols&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;50&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;textarea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;4-textarea-auto-size&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-textarea-auto-size&quot; aria-label=&quot;4 textarea auto size permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Textarea auto size&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://material.angular.io/cdk/categories&quot;&gt;Angular CDK&lt;/a&gt; has a &lt;a href=&quot;https://material.angular.io/cdk/text-field/overview&quot;&gt;text-field&lt;/a&gt; package that provides useful utilities for working with text input. One of them was &lt;code class=&quot;language-text&quot;&gt;cdkTextareaAutosize&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;cdkTextareaAutosize&lt;/code&gt; directive can be applied to any &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&lt;/code&gt; to make it automatically resize to fit its content. The minimum and the maximum number of rows to expand to can be set via the &lt;code class=&quot;language-text&quot;&gt;cdkAutosizeMinRows&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;cdkAutosizeMaxRows&lt;/code&gt; properties respectively.&lt;/p&gt;
&lt;p&gt;The resize logic can be triggered programmatically by calling &lt;code class=&quot;language-text&quot;&gt;resizeToFitContent&lt;/code&gt;. This method takes an optional boolean parameter force that defaults to false. Passing true will force the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&lt;/code&gt;to resize even if its text content has not changed, this can be useful if the styles affecting the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;textarea&gt;&lt;/code&gt; have changed.&lt;/p&gt;
&lt;p&gt;I imported the &lt;code class=&quot;language-text&quot;&gt;TextFieldModule&lt;/code&gt; into my module and then applied the &lt;code class=&quot;language-text&quot;&gt;cdkTextareaAutosize&lt;/code&gt; directive to my textarea, that’s all!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;textarea&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[(ngModel)]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;textarea-inline-control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;cdkTextareaAutosize&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;textarea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See the result&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/69ec33b14dc6461749381c71cc4d6769/06.gif&quot; alt=&quot;Angular Jira Clone Part 04 - Build an editable textbox&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;5-emit-event-onblur&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-emit-event-onblur&quot; aria-label=&quot;5 emit event onblur permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Emit event onBlur&lt;/h3&gt;
&lt;p&gt;Now the UI looks very good. What you need to do is simply listen to the onBlur event of the textarea and then somehow emit it to the parent component, or dispatch an event to update from there. That part I leave to you like the homework 🤣 That’s how I did for the &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/issues/issue-title/issue-title.component.ts&quot;&gt;issue-title&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IssueTitleComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnChanges&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; issue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JIssue
  titleControl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormControl

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _projectService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnChanges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;changes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SimpleChanges&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; issueChange &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; changes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issue
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;issueChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentValue &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; issueChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;titleControl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FormControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;onBlur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_projectService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateIssue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;titleControl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I simply set up a form control when I received a new value from the parent component, update the issue on the blur. When it is updated, ngOnChanges will be triggered and then set the new value to the control If there are any changes. Pretty simple, no? 🤣&lt;/p&gt;
&lt;p&gt;That’s all for the fourth part. Any questions, you can leave it on the comment box below or reach me on Twitter. Thanks for stopping by!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 03 - Setup Akita state management]]></title><description><![CDATA[My third tutorial will go further into the application state management with Akita]]></description><link>https://trungvose.comangular-jira-clone-tutorial-03-akita-state-management/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-03-akita-state-management/</guid><pubDate>Tue, 01 Sep 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My third tutorial for &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt; will go further into the application state management with Akita.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;state-management-overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#state-management-overview&quot; aria-label=&quot;state management overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;State management overview&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;State management makes the state of your app tangible in the form of a data structure that you can read from and write to. It makes your ‘invisible’ state clearly visible for you to work with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;what-is-state&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-state&quot; aria-label=&quot;what is state permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is state?&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;I took some ideas from &lt;a href=&quot;https://egghead.io/articles/what-is-state-why-do-i-need-to-manage-it&quot;&gt;What is state? Why do I need to manage it? by Raquel Moss&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You might have a &lt;a href=&quot;https://medium.com/super-declarative/understanding-state-management-and-why-you-never-will-dd84b624d0e&quot;&gt;different definition for “state”&lt;/a&gt; though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data (whatever that is!).&lt;/li&gt;
&lt;li&gt;All the variables in all the components.&lt;/li&gt;
&lt;li&gt;All the variables in the entire application.&lt;/li&gt;
&lt;li&gt;A store, like a Redux or Flux store.&lt;/li&gt;
&lt;li&gt;The behavior of the app at a given moment in time (this is my definition!). Or in the other words, &lt;strong&gt;the output of all the actions that the user has taken since the page loaded&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, I load a page with a button on it. I click this button to make an HTTP request. The HTTP request fails and there should be an error display on the screen.&lt;/p&gt;
&lt;p&gt;What is the action? A button was clicked, it sent a request, and the request was failed.
What is the outcome? An error message.&lt;/p&gt;
&lt;p&gt;The old way is you will store the error message inside the component itself. But what if you want other components to display this error instead? It will lead to the need of sharing data between different components.&lt;/p&gt;
&lt;p&gt;If we were to describe a shared object that holds all of the application data, it might look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  error&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;There was an error while fetching the data. Please try again&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Imagine you have some sort of service that store this data instead of storing it on the local state of the component. This means others will also be able to access it as well, right?&lt;/p&gt;
&lt;h3 id=&quot;what-is-state-management&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-state-management&quot; aria-label=&quot;what is state management permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is state management?&lt;/h3&gt;
&lt;p&gt;State management library provides us with tools to create the data structure as we mentioned above, and a way to update them when the new actions occur. You could use a simple service for that purpose. But when the application is getting more complicated, you would want to have state management, seriously.&lt;/p&gt;
&lt;p&gt;Without a state management system, how do we know what the state of our application is? We look at the DOM. We can check DOM elements to see if they have certain classes (‘active’, ‘error’), or to check whether certain elements exist, or manually debug your code and do &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With a state management system, to find out what the state of our application is, we check our state data structure inside their &lt;a href=&quot;https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en&quot;&gt;compatible development tool&lt;/a&gt;. The UI should reflect the data, but the data is the &lt;strong&gt;Source Of Truth&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;why-would-i-want-to-use-state-management&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-would-i-want-to-use-state-management&quot; aria-label=&quot;why would i want to use state management permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why would I want to use state management?&lt;/h3&gt;
&lt;p&gt;To synchronize the data model and the UI &lt;strong&gt;has always been a difficult task&lt;/strong&gt; previously. If you work with jQuery, every click that update the UI, you also have to update the JavaScript data model. All of the new UI frameworks included Angular/React or Vue made this much easier by only updating the data, and they will reflect the changes to the UI.&lt;/p&gt;
&lt;p&gt;Say you have a boolean to display a component based on that value. If it is true, you display the button and vice versa. With Angular, you only need to toggle the boolean on UI change, the display of component will be automatically handled (assume that you still attach the change detector of the UI framework)&lt;/p&gt;
&lt;p&gt;You have to realize that there is always a state of an application - data get loaded from the server, some parameters on the URL, users perform actions and things change in response to those actions.&lt;/p&gt;
&lt;p&gt;When you’re creating larger and more complex JavaScript applications, having explicit data to work with predictably is a huge boon to developers. It’s much easier to reason about and manipulate, and it’s less bug-prone (though of course, you can still create a lot of bugs with any code you write with a state management system).&lt;/p&gt;
&lt;h3 id=&quot;when-you-should-use-state-management&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-you-should-use-state-management&quot; aria-label=&quot;when you should use state management permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When you should use state management?&lt;/h3&gt;
&lt;p&gt;I use state management when I have a tree of components with many layers that need to interact with the same piece of data over time. See the screenshot capture from my &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e9bbb36edfb86eecaa5fc7f1190ed1d4/525d3/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 70%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADM0lEQVQ4y1WTS28bVRTH50OwBIkFi0YlOA+oWKAmxg4mSUVSohZ1wzfgU7BgwR41aZrYSQhqUctDLIqouuimCLpBQqRSsCdxxo7Hnrl37rzHM+NfNZNNe6T/PUfn6P7PQ+doC0urzF75kMr8+1yemaMyM01lfp6p9yq8c2mKyswM07NzvDs7x9R0hY8WFqk1GtQ+WebjWp25Kx9wuTJDZW6+5NCWr12n3lhhbW2NRmOZ5U8b1Ot1rl6tUqvVWFios7i4wOJinWq1SrW6xPX1a9y8cYsbaxssLS2xvLLCxucbrK5+hnbpy/v8/NtfiG6bY71Lr9fHNE2kdJBSYguJ6yqko/A8F9d1UUrhOE4ZK1DYFz6F9uYXD/jpSRslFIapENJDSAchFSNL4vsBTHKSOCGJIvwgIIrHeH6AH4QkaU6UpERxQhDGaG+s3+Pw8THScen2rZLEFoJxmpHlE4QQPH/6B48OD+k8e0Y2gSiKSdOUIIzIsoxCJpNJ8aK9tb7DwycvCHwPW7q4no8lHAw75syOcIME1/WwLYEjFUFQVBnhegFhFJfV+kFc2gW0t2/+wI+PX+BIyWAkUa5PGIWMLMG5aZWtFCjsAgVZMY5e38QWqhzJaGiW7Zct3/rqG77+9ju27myzfXeH3eYhrdY9ms199vYO2D9o0Wy22NzaZnPrbunb2W2x29xjf/+A27c3ubO1zd7e97Ra+2j22THnnTOG5kVGyzpiOHyObfex7RFCjLFtWc6ygGVZdLtdTk9PabfbnOgdTk8M9HaXTkdHS8Y5/sAjMItWIYz/wfV/Jwh+IYoeEce/ApJX5bx3zqA3KNfLsYd4rrpYLSnQ0mxM4sZI3SZLxq98SwGPCWevkeV5erGnfYF9buEolzhOiOOYJEkKwhR36NH/10QOfJQToZwQ5cR4CqT9N8PBfQLfJvJDlAwRtmBkWAz7FnmevZZQyycZ0vQ4enrK8Z8G3f8dDN3F0BWG7mHoBoZ+RLezSf/kP3p6gD2y8Z2QoRXieClSJQgnoRifVpxNGAQkYcgky8iyIhAxTiPGpU7LJY+TqPTHSUgUhaRxWl7POM1J07zUeZ7zEo3muINkBQlSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        title=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        src=&quot;/static/e9bbb36edfb86eecaa5fc7f1190ed1d4/d9199/01.png&quot;
        srcset=&quot;/static/e9bbb36edfb86eecaa5fc7f1190ed1d4/8ff5a/01.png 240w,
/static/e9bbb36edfb86eecaa5fc7f1190ed1d4/e85cb/01.png 480w,
/static/e9bbb36edfb86eecaa5fc7f1190ed1d4/d9199/01.png 960w,
/static/e9bbb36edfb86eecaa5fc7f1190ed1d4/525d3/01.png 1090w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I have at least &lt;u&gt;5 levels&lt;/u&gt; of the component hierarchy. When I first load the application, the data was fetched at the first level component - &lt;code class=&quot;language-text&quot;&gt;ProjectComponent&lt;/code&gt;. And on the fifth level component - &lt;code class=&quot;language-text&quot;&gt;BoardDndListComponent&lt;/code&gt;, I need to access to the data from the first level component as well.&lt;/p&gt;
&lt;p&gt;So if you only do Input and Output to pass the data between component, it will be a nightmare to handle the data change, you have to pass the data through 5 different levels and some component in the middle might not need the data at all, but because the fifth level needs it, so the data need to go through the whole flow. Think about it like&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/563bad46f110ebf8d02c938e072e1cb5/01645/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABZElEQVQoz3WRS2/TUBCF/d9LImjrR3htgBVLNuzYIlYIBD+gUtqQOvfWvo7fj/uw/aFYkaASHOnTmZnNHM14T59dcnXt4/sBQfiSKHpLEL4iDF8ThBFBEOEH4VJf+wFhuFn6aPOc1XrNxcWK9ZMr1qtLNpsXeHQzf1QAn89e8z9ZZ7HWYI2BeYL5ZDPGGLxSFGRbRa4Gmi6mrL5Slp84Zu8p8ne09RdGB9ZqpmlCKcX2Zsuv2z3x/Z5UxhyPGWmaIoTAs52h2heM2v4jSwUMjyZNW5PJjGJ/RElF1/c4Ny4Mw4BXpg133w+Im5xUtqSyOVOh5Iy4/8hh94FM3JKnChnnNGVNvJNobR6fwlq8tuy5+yHY/ZSIXUUqO1LRnulIDg8kB0Ei3pA9fCORPX3dkCUNKtdUjSMvDWVtlwVe2zQMbcfoHM4ZtO7R5m8M2lhOYbRxdH2L1RZnLMY4rJuwdlz89JTf0mUAs5i1v8gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        title=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        src=&quot;/static/563bad46f110ebf8d02c938e072e1cb5/d9199/02.png&quot;
        srcset=&quot;/static/563bad46f110ebf8d02c938e072e1cb5/8ff5a/02.png 240w,
/static/563bad46f110ebf8d02c938e072e1cb5/e85cb/02.png 480w,
/static/563bad46f110ebf8d02c938e072e1cb5/d9199/02.png 960w,
/static/563bad46f110ebf8d02c938e072e1cb5/01645/02.png 1091w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That’s when you want to have a &lt;strong&gt;state management&lt;/strong&gt; in your application. If you have one, it will look like. Basically, if any component wanted to get the data, it will talk directly to the state management. You will see how it looks later on when I go deeper to Akita.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cf7426bfeb6186cbcc0850d8b7ce9d32/01645/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABkUlEQVQoz2WRS2sUURCF+8e59V+4dqmLgEwmaJLpacVEBDcuxI0YV1lk6wsEwSCCG1GTzkz3nb493XP7fR/p+aTHQBAPdThVtag6RXn3H+yytz9hMvHx/ccEwTP86QHT6SH+NGDiBwTBQ7ZG+9y6PeLeeEIQDL1HbG+PGY3G7Iz32BnvcnD4BI96zTUK4ANQXvEar98l3LjzlaO3EbCm1RprDaz7Iej7NcYYPCUUy5+SPO0omwhVfEKpE7LsJav8OcXq/Wbgq5Pv3Lz7kRfH30hmIWe/QuazECnmZFlGkiQIIfBsaynCHNcZ/sfg2P5N14MLR1EqlmLJ6iIjXaS0XYdzlxt2XYdXLCt+f46IfuQkcY2MK2RcIuOWJJ4xP3+KjE7JxAUqaRBRSqVK5ucCa+0/64faa4qOsy8x4alAhAVy0SBFjRSDKhaRRIpjZPyGfKFJREZbNiyTmjTXqMqSK4MqLVobvLqqaOuGS+dwzqB1izbtlXZoY9GmR+ueTrc0bYM1Fmcsxjqs67F2OLnfPOUP/bf6HzSfNbEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        title=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        src=&quot;/static/cf7426bfeb6186cbcc0850d8b7ce9d32/d9199/03.png&quot;
        srcset=&quot;/static/cf7426bfeb6186cbcc0850d8b7ce9d32/8ff5a/03.png 240w,
/static/cf7426bfeb6186cbcc0850d8b7ce9d32/e85cb/03.png 480w,
/static/cf7426bfeb6186cbcc0850d8b7ce9d32/d9199/03.png 960w,
/static/cf7426bfeb6186cbcc0850d8b7ce9d32/01645/03.png 1091w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;when-you-should-not-use-state-management&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-you-should-not-use-state-management&quot; aria-label=&quot;when you should not use state management permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When you should not use state management?&lt;/h3&gt;
&lt;p&gt;For some simple application like CRUD, say you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A component to display a list of resource&lt;/li&gt;
&lt;li&gt;A detail view that will open when you select an item from the resource list&lt;/li&gt;
&lt;li&gt;A form when you click edit somewhere on the detail view&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you think about it, you don’t need state management for this kind of application. Because usually each component I mentioned above was configured with their route and only display a single component at the same time. I would rather make a direct API call to the server every time the view is loaded than store it on the state management. Below was one of the similar application that I built &lt;strong&gt;without&lt;/strong&gt; state management at Zyllem.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/5ee5a3197f1dbb3bfede8e4d024cd8a6/04.gif&quot; alt=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Still CRUD app, but multiple components nested through tabbed view as below screenshot. You &lt;strong&gt;would want&lt;/strong&gt; to have state management 😂 So it is really depends your requirement.&lt;/p&gt;
&lt;p&gt;Remember that you’re building something for your company, your product, and your customers. The business rules and relationships within your product, company, and industry are far more important then whether or not you &lt;a href=&quot;https://medium.com/super-declarative/understanding-state-management-and-why-you-never-will-dd84b624d0e&quot;&gt;force a state management into your app&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/f345536d6c1513e0a9c3ea467bfb34b9/05.gif&quot; alt=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;integrate-akita-to-jira-clone&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#integrate-akita-to-jira-clone&quot; aria-label=&quot;integrate akita to jira clone permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Integrate Akita to Jira Clone&lt;/h2&gt;
&lt;h3 id=&quot;what-is-akita&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-akita&quot; aria-label=&quot;what is akita permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is Akita?&lt;/h3&gt;
&lt;p&gt;Akita is a state management pattern, built on top of RxJS, which takes the idea of multiple data stores from Flux and the immutable updates from Redux, along with the concept of streaming data, to create the Observable Data Store model.&lt;/p&gt;
&lt;p&gt;Akita encourages simplicity. It saves you the hassle of creating boilerplate code and offers powerful tools with a moderate learning curve, suitable for both experienced and inexperienced developers alike.&lt;/p&gt;
&lt;p&gt;That’s why I decided to go with Akita for Jira clone.&lt;/p&gt;
&lt;h3 id=&quot;jira-clone-simple-interactive-flow-with-akita&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jira-clone-simple-interactive-flow-with-akita&quot; aria-label=&quot;jira clone simple interactive flow with akita permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jira clone simple interactive flow with Akita&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/assets/img/diagram/interaction-data-flow.png&quot; alt=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;&gt;&lt;/p&gt;
&lt;p&gt;I set up a &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/app/project/state/project/project.store.ts&quot;&gt;project state with initial data&lt;/a&gt;. The main heavy lifting part I think is the &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/app/project/state/project/project.service.ts&quot;&gt;project service&lt;/a&gt;, it contains all the interacting with &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/app/project/state/project/project.store.ts&quot;&gt;project store&lt;/a&gt;. Such as after fetching the project successfully, I update the store immediately inside the service itself. The last lego block was to expose the data through &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/app/project/state/project/project.query.ts&quot;&gt;project query&lt;/a&gt;. Any components can start to inject &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/app/project/state/project/project.query.ts&quot;&gt;project query&lt;/a&gt; and consume data from there.&lt;/p&gt;
&lt;p&gt;If you are using ngrx, you have to dispatch an action when you started fetching the project, and then there is an effect somewhere that was detached from your context need to handle the action, send a request to the API server. And finally, the effect will tell whether the data was successfully fetched or not. &lt;u&gt;There is nothing wrong with ngrx approach&lt;/u&gt;, it is just too much concept and layer that you need to understand. To be honest, I used to afraid of integrating ngrx in my project because of the unnecessary complexity it would bring.&lt;/p&gt;
&lt;h3 id=&quot;1-install-akita&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-install-akita&quot; aria-label=&quot;1 install akita permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Install Akita&lt;/h3&gt;
&lt;p&gt;Follow the &lt;a href=&quot;https://datorama.github.io/akita/docs/installation&quot;&gt;installation guide&lt;/a&gt; to add Akita to Angular CLI project. Simple run&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ng &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; @datorama/akita&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It will ask you some questions and did a bunch of work for you. Just selected what you need, I don’t want the Router store and Firebase.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4ab2200e2a50f98c7a2fe1d9a8e56398/37523/06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABj0lEQVQoz3WQy5aTQBRF8xnaVJEmSQNFeIWHFEkIFI9u7Thx4sz//4ztArtdUeNgr3Nrsuueu3pK9ninnP2gicuSY9QQ7h3igyBKbLJckh4EYSiJYskhE8SxXLBViPAj5Jz7BOkqVptM4Z0inGOK06Q85gqrDPmQ+HwMXSxhYz3YWNYvHm5mKSzsd6w5BatUF3x5vdJ0A8b0XLqefpjIdI2rFJYUSCmwbfsNeYON/IvVPosZrx0vz1emcaI3I6+fvzL2E8P4TKbPuJ6HEBZCzPI3kZS/51tWtm+R/ohozx3DNGL6gc4Yqqri0l5o25ZKa4qy5FNVkSQJm83mrmwROrtHhmtL1xqGaaLtDKdzQ14U+Eqx2W7Z7XYopfB9H8dx/tn0D+HTbsv3by+LzPQ9uq4xZkDrmjRNyfJ8kc3SmfkTFQR3ZYuw1JqmqWmahtP5TKVr8iqn0AVJkuK6Lq7r4fn+Ipzfc2V7vuG9yn4U0lxa6vpIZzryrCDUIUVfkETJso1SAUEQLLI51+v1fyv/BH0hDUGIpPFXAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        title=&quot;Angular Jira Clone Part 03 - Setup Akita state management&quot;
        src=&quot;/static/4ab2200e2a50f98c7a2fe1d9a8e56398/37523/06.png&quot;
        srcset=&quot;/static/4ab2200e2a50f98c7a2fe1d9a8e56398/8ff5a/06.png 240w,
/static/4ab2200e2a50f98c7a2fe1d9a8e56398/e85cb/06.png 480w,
/static/4ab2200e2a50f98c7a2fe1d9a8e56398/37523/06.png 720w&quot;
        sizes=&quot;(max-width: 720px) 100vw, 720px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-set-up-project-store&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-set-up-project-store&quot; aria-label=&quot;2 set up project store permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Set up project store&lt;/h3&gt;
&lt;p&gt;A Store is a single object which contains the store state and serves as the single source of truth.&lt;/p&gt;
&lt;p&gt;To create a store, you need to extend Akita’s Store, passing the type as well as its initial state. See how I create my &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/app/project/state/project/project.store.ts&quot;&gt;project store&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProjectState&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JProject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createInitialState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectState &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    issues&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    users&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ProjectState
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Injectable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  providedIn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;StoreConfig&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;project&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProjectStore&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Store&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ProjectState&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createInitialState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s how my &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/interface/project.ts&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;JProject&lt;/code&gt;&lt;/a&gt; model looks like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JProject&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  category&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectCategory
  createdAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  updateAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  issues&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JIssue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  users&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JUser&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This object has everything I need to display the UI. In reality, it might be much more different because this object could be big. It will increase the fetching time from the server that leads to a slow response to the user. You might want to break it into different API calls in the actual application. I set the initial &lt;code class=&quot;language-text&quot;&gt;issues&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;users&lt;/code&gt; to be an empty array. Next, you want to query the data from the store.&lt;/p&gt;
&lt;h3 id=&quot;2-set-up-project-query&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-set-up-project-query&quot; aria-label=&quot;2 set up project query permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Set up project query&lt;/h3&gt;
&lt;p&gt;A Query is a class offering functionality responsible for querying the store. You can think of the query as being similar to database queries. Its constructor function receives as parameters its store and possibly other query classes.&lt;/p&gt;
&lt;p&gt;Queries can talk to other queries, join entities from different stores, etc. To create a Query, you need to extend the &lt;strong&gt;Query&lt;/strong&gt; class from Akita:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProjectQuery&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ProjectState&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; store&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectStore&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  isLoading$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  all$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  issues$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;issues&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  users$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;users&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  issueByStatusSorted$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IssueStatus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Observable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;JIssue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issues$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;issues &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; filteredIssues &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; issues
          &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;listPosition &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;listPosition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; filteredIssues
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here I have a few selectors for displaying into the UI. I need to also filter a list of issues before sending them to each lane (Backlog, In Progress, and the link), using a simple RxJS operator helps me to do so easily. Now come to the important part, update the store on performing some action. You need a service for that purpose.&lt;/p&gt;
&lt;h3 id=&quot;3-set-up-project-service&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-set-up-project-service&quot; aria-label=&quot;3 set up project service permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Set up project service&lt;/h3&gt;
&lt;p&gt;In the &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/frontend/src/app/project/state/project/project.service.ts&quot;&gt;project service&lt;/a&gt;, we’ll update the store when&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make our API calls&lt;/li&gt;
&lt;li&gt;Interact with the UI
&lt;ul&gt;
&lt;li&gt;Drag and drop issue&lt;/li&gt;
&lt;li&gt;Update issue&lt;/li&gt;
&lt;li&gt;and so on&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Anything that will result in a data change, you have to do it inside the service. You should not modify the object on the local component itself.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Injectable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  providedIn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProjectService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  baseUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; HttpClient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _store&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectStore&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;baseUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; environment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;apiUrl
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;setLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getProject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_http
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;JProject&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;baseUrl&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/project.json&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setLoading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_store&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;project &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;project&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;catchError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_store&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;4-use-store-and-service-for-update-data&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-use-store-and-service-for-update-data&quot; aria-label=&quot;4 use store and service for update data permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Use Store and Service for update data&lt;/h3&gt;
&lt;p&gt;After finishing set up three pillars: store, query, and service. Each one of them is marked as &lt;code class=&quot;language-text&quot;&gt;@Injectable({ providedIn: &apos;root&apos; })&lt;/code&gt;. It means that the store, the query, and the service are app-wide singletons, and therefore can be accessed everywhere in our application. For example, in components, directives, services, and queries.&lt;/p&gt;
&lt;p&gt;You can start using them into your component by injecting it into the component constructor. See how I render the list of issues to the UI and update them on drag and drop.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/board/board-dnd/board-dnd.component.ts&quot;&gt;board-dnd.component.ts&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BoardDndComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  issueStatuses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IssueStatus&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    IssueStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BACKLOG&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    IssueStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SELECTED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    IssueStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IN_PROGRESS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    IssueStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DONE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; projectQuery&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectQuery&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; authQuery&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AuthQuery&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/board/board-dnd/board-dnd.component.html&quot;&gt;board-dnd.component.html&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flex container mt-7&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDropListGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;board-dnd-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;board-dnd-list&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let status of issueStatuses&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[status]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[currentUserId]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;authQuery.userId$ | async&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[issues$]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;projectQuery.issueByStatusSorted$(status)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/board/board-dnd-list/board-dnd-list.component.ts&quot;&gt;board-dnd-list.component.ts&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BoardDndListComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  IssueStatusDisplay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; IssueStatusDisplay
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IssueStatus
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; currentUserId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; issues$&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Observable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;JIssue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  issues&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JIssue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;issuesCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issues&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _projectService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectService&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _filterQuery&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FilterQuery
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;combineLatest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issues$&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_filterQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all$&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;untilDestroyed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;issues&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filter&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issues &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filterIssues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;issues&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CdkDragDrop&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;JIssue&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newIssue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; JIssue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newIssues &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousContainer &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;moveItemInArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newIssues&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateListPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newIssues&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;transferArrayItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousContainer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        newIssues&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;previousIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentIndex
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateListPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newIssues&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      newIssue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; IssueStatus
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_projectService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateIssue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newIssue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s all for part 3. Any questions, you can leave it on the comment box below or reach me on Twitter. Thanks for stopping by!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 02 - Build the application layout with flex and TailwindCSS]]></title><description><![CDATA[My second tutorial will walk you through how to build the application layout that has navigation, sidebar and content section]]></description><link>https://trungvose.comangular-jira-clone-tutorial-02-application-layout-tailwindcss-flex/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-02-application-layout-tailwindcss-flex/</guid><pubDate>Thu, 27 Aug 2020 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My second tutorial for &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt; will walk you through how to build the application layout that has navigation, sidebar and content.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;navigation-section&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#navigation-section&quot; aria-label=&quot;navigation section permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Navigation Section&lt;/h2&gt;
&lt;p&gt;If you look at the &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;final application&lt;/a&gt;, you will see that there are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A left navigation bar to display the logo and some icons on top, with user profile and about icon at the bottom&lt;/li&gt;
&lt;li&gt;A sidebar next to the navigation bar that will be collapsible that display some navigation on the application&lt;/li&gt;
&lt;li&gt;A resizer that can toggle the sidebar collapsible&lt;/li&gt;
&lt;li&gt;A content section to display either a drag and drop board or a form&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was visualizing them as three separate components and that is how the mockup looks like with three columns.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 795px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABuElEQVQoz2O4+fLsrhNnu+bu7l+8d+LivSsPXJ134Pb8A7fwoAUHbs3Zd/PW42cMu+4uLuhfzKhXw2fdKGTd4FqzTb/+qGHdIcO6w7iQcf1hrapDuy8+YNh3f2XV9BUitq0Knp0qnh3BrbvsOk46tB9zaD+OCzl2HLdqPrr/8kOGvfdWVExbLmTTIufRqeTRHtC807rthG3rUdvWYxBk13rMrg1EIkTajpk3Ht13+SHDgYerKqet4LNoknBqlXNpDWzZhabZqvmoZdNRq5ajWDSvPT2zbPIy/eAprhlzzSMn+aPabNd2LHTK2ajp5/wnnLbB1LzjxuLyqculnDq0g/o1fbswne3adcKj+6Rz53EsNu97sLJi2gp+yyYplzZ5t7YADGfbtBy1bjlqg9XZe++vqJi6XNC6Rda9Q9G9PaAF3WZMRDXNKyunrRC2aZX36FT26Ahq3WXbfsK+7ah92zFcyKH9mGUTWPOee0uLJy1hN6kXcWiRcGj2qt9u0njMrOGwWcMRXMi88Yh+zeE9lx4wnHu6b+HWfZHly5LqV6TWr2hbcap8xeWKFZfwoMoVl0qWXjx/9wkA1P6h/rdcmfgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 02&quot;
        title=&quot;Angular Jira Clone Tutorial Part 02&quot;
        src=&quot;/static/2c1dfe3541d30809bbce6e8a31a6f624/65c7b/01.png&quot;
        srcset=&quot;/static/2c1dfe3541d30809bbce6e8a31a6f624/8ff5a/01.png 240w,
/static/2c1dfe3541d30809bbce6e8a31a6f624/e85cb/01.png 480w,
/static/2c1dfe3541d30809bbce6e8a31a6f624/65c7b/01.png 795w&quot;
        sizes=&quot;(max-width: 795px) 100vw, 795px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Because I only need to support one dimension layout, I choose to use flex for that purpose. In the most simple form, that is how the navigation component looks like.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/navigation/navigation/navigation.component.html&quot;&gt;navigation.component.html&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;navigation&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flex flex-row overflow-hidden h-full&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-navbar-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-navbar-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-sidebar&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[expanded]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;expanded&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-sidebar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-resizer&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;(click)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;toggle()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[expanded]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;expanded&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-resizer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.navigation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-custom-tailwindcss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-custom-tailwindcss&quot; aria-label=&quot;1 custom tailwindcss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Custom TailwindCSS&lt;/h3&gt;
&lt;p&gt;I need the left navigation to have 64px width and the sidebar 240px. I go ahead and modify the &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/tailwind.config.js&quot;&gt;tailwind.config.js&lt;/a&gt; to have an additional two spacing needed.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 629px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAABcElEQVQoz42QS27jMBBEdY8x2f9uUtTHsp3Y8coBZrIczP1PM5CSTRADduEtnggUimJH5q/n88vpdHm7tNZU3T3MPKJEhKpv2F06FO1bG9Y0iybehta7R0RprQfA3S7lNfCTDgBSSoTExLJGmVdRlU8xMyLK97KWEdHnMfZTLFOdhuKsqmamZpu4MN8v47pJMc7L7aMuR/FCamn36xtpl+/RAaIwxXg8/P43Xt61zhxTlgCJLJ7Fv4R/IL4uq2oZlv5wVeszO2pBq2AVN75Lj1+fPWjpiNnXlOVyO71ekQghMyMCMCETEiIhbALM9HnChADbP69vQ6Kl1Wn/MvYqnFJ+Jh0zRymCbG1q+8PHcVj6snuyTLQti7EXMZ+Lh0rKkOExHbOUEl6mer7Z8gZ1ST5AjCv+gE5ELULF6jAP00FZJCdOGZ+5tqnFMArr1I/XP3/n4+Xc6rmVAHjYX5fDXUlq9Mf94XUag3lgUoCHy/8BPut8NJe/RUAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 02&quot;
        title=&quot;Angular Jira Clone Tutorial Part 02&quot;
        src=&quot;/static/d3c1c4e2198697fbf44eaa5aa60a458d/63a68/02.png&quot;
        srcset=&quot;/static/d3c1c4e2198697fbf44eaa5aa60a458d/8ff5a/02.png 240w,
/static/d3c1c4e2198697fbf44eaa5aa60a458d/e85cb/02.png 480w,
/static/d3c1c4e2198697fbf44eaa5aa60a458d/63a68/02.png 629w&quot;
        sizes=&quot;(max-width: 629px) 100vw, 629px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;By default the &lt;a href=&quot;https://tailwindcss.com/docs/customizing-spacing&quot;&gt;spacing scale&lt;/a&gt; is shared by the padding, margin, width, and height utilities so the above configuration would generate classes like &lt;code class=&quot;language-text&quot;&gt;.p-2, .mt-3, .w-5, .h-6&lt;/code&gt; and my custom one &lt;code class=&quot;language-text&quot;&gt;w-sidebar&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;w-navbarLeft&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;2-build-the-left-navigation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-build-the-left-navigation&quot; aria-label=&quot;2 build the left navigation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Build the left navigation&lt;/h3&gt;
&lt;p&gt;After configuring the custom spacing for Tailwind, I start to build up the left navigation component.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/src/app/project/components/navigation/navbar-left/navbar-left.component.scss&quot;&gt;navbar-left.component.scss&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.navbarLeft-content&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@apply&lt;/span&gt; h-screen w-navbarLeft pt-6 pb-5 flex flex-col bg-primary flex-shrink-0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.logoLink&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@apply&lt;/span&gt; relative pb-2 flex items-center justify-center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s the simplified version of the left navigation HTML code. I have an aside that wrap a &lt;code class=&quot;language-text&quot;&gt;.navbarLeft-content&lt;/code&gt; class which I have some style defined above: set full height of the viewport (&lt;code class=&quot;language-text&quot;&gt;100vh&lt;/code&gt;), some &lt;code class=&quot;language-text&quot;&gt;margin&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;padding&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;flex&lt;/code&gt; style.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 535px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABuUlEQVQoz3WSiYrbMBCG/RyNdc6h25Kdw3bSTbssFPr+L1TUZEtJ6fAxEpI++BEzKKW0NqG0WGpdTlPOLcdjLa2kGAMRee9DCFopKaVSsi+fNdhHccR8Spf7vN+3Eu5z3kosjpqnyDAKOQoxSnkYhRDiRTZyPCg5GnBc5ny+thS+1XBJLoImo8lqqyVqGcGgUVKMD7qMxNpPxlfNCeuWv/6cbx+37Xo5Xk7r23Ze1+N5O6/3t/dtv/s8Ky4PBiICRAWs0Wn0lBZqNwiTBA/IBl3vwEiO2B8MjYakfTIwszU9ipLCWLDo5HiQ4xc5HsSTXo/9n8DP2N45Yqd9077avOJ8jftHnPdU5jwtqSypzK0dbWjK1RcGYrLWKmOVMZQWrhuliZ13RMmxIwKXAFkqI/UrPTYia86/f+vqTt/L7Uc7ruu85DJTrCafta8Ko6L0wuC9J3rImVqXua4utZwnTVlQkhj+1T5l55FYcdYUse7u9O6Xq8/LVJr1pT/i/F+ZnQOwRmsKE4eijVVSyE6fxCfqs6u/T8QQQkQCTtXPe0hRCgGAfXIQAQEArAUktABIhIjWAth+Yaz9BSnXdEjmbPGhAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 02&quot;
        title=&quot;Angular Jira Clone Tutorial Part 02&quot;
        src=&quot;/static/958d68cfa6f74a1530f04c3ada90de1c/b5245/03.png&quot;
        srcset=&quot;/static/958d68cfa6f74a1530f04c3ada90de1c/8ff5a/03.png 240w,
/static/958d68cfa6f74a1530f04c3ada90de1c/e85cb/03.png 480w,
/static/958d68cfa6f74a1530f04c3ada90de1c/b5245/03.png 535w&quot;
        sizes=&quot;(max-width: 535px) 100vw, 535px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To go into every single detail on CSS is difficult because It would make the blog post too long. You should be able to figure it out by yourself 💪💪💪&lt;/p&gt;
&lt;h3 id=&quot;3-sidebar&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-sidebar&quot; aria-label=&quot;3 sidebar permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Sidebar&lt;/h3&gt;
&lt;p&gt;It is very similar to what I have done for the left navigation, still using flex. But It poses an interesting topic, scrollable content. Let see how it looks on the below screenshot&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/6c45b9fc5eb3d6ba45525cbef006882e/04.gif&quot; alt=&quot;Angular Jira Clone Tutorial Part 02&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Basically, you wanted to have a y-scroll appear when there is not &lt;strong&gt;enough space&lt;/strong&gt;. But it was tricky sometimes.&lt;/p&gt;
&lt;h3 id=&quot;4-scroll-able-container-with-dynamic-height-using-flexbox&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-scroll-able-container-with-dynamic-height-using-flexbox&quot; aria-label=&quot;4 scroll able container with dynamic height using flexbox permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Scroll-able container with dynamic height using Flexbox&lt;/h3&gt;
&lt;p&gt;I found an excellent article &lt;a href=&quot;https://medium.com/@stephenbunch/how-to-make-a-scrollable-container-with-dynamic-height-using-flexbox-5914a26ae336&quot;&gt;How to make a scrollable container with dynamic height using Flexbox by @stephenbunch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the hidden features of &lt;a href=&quot;https://css-tricks.com/snippets/css/a-guide-to-flexbox/&quot;&gt;Flexbox&lt;/a&gt; is the ability to make a flex child scrollable. In the past, if you wanted to make a scrollable container, you had to give the &lt;u&gt;container a predefined height&lt;/u&gt;. In other words, the height could not be based on the size of its surrounding content. You had to use pixels, percentages, or absolute positioning if you wanted an element’s content to scroll.&lt;/p&gt;
&lt;p&gt;With Flexbox, we can now create scrollable containers whose size depends on the available space after the rest of the content has been laid out.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The key is to use Flexbox for all containers that wrap the scrollable container and give the outermost container a predefined height. Since content lays vertically on the page by default, I recommend making each container use the vertical (column) flex layout rather than the default horizontal (row) layout.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;See the example below for how it should look.&lt;/p&gt;
&lt;iframe height=&quot;378&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;Scrollable container with dynamic height&quot; src=&quot;https://codepen.io/stephenbunch/embed/KWBNVo?height=378&amp;theme-id=light&amp;default-tab=css,result&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&apos;https://codepen.io/stephenbunch/pen/KWBNVo&apos;&gt;Scrollable container with dynamic height&lt;/a&gt; by Stephen Bunch
  (&lt;a href=&apos;https://codepen.io/stephenbunch&apos;&gt;@stephenbunch&lt;/a&gt;) on &lt;a href=&apos;https://codepen.io&apos;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;p&gt;That was what I have done for the layout, I started making the container display at the very top body level down to app-component and way below until I reached to the scrollable container.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAC9UlEQVQoz12TS4/bZBiF/TMoTQMTx5dcHDtOnGQumoIIt2aCChUIVHUB7NghlkWaS2eKxL9gj+iCBV0iKjS5DOsKkCohMpNpYsexk9i5zgNxJRa80iO93+bVOTrnEwpWGaNgoRl54hsJXo7FuRa7wUvXX3BtTSxOQpKR1TSirCKpKUzLolguYZZ0DNOkUCpj5AsI978+4JuHJzw8Oebk+JjDwyMeHB1ydHTCwcEBxw+O2d/fp1p9Cz2nUSiUKFe22d19jZ2dXcqVMttbW9y8+TqbmzsIveGMq9WKK2C5XLJcrpjPF/x/6vU6kiSh6zo5XUfL5VDVFJKkICaTJJMSsqwg/PlXn855l9l8ge0M6Dkuo/GE8SQgCKeE0zlhEHBrrx7ZTqVSSEkRUVyTiEiu90QCRVEQLuzJfyqe/fGUJ49/pPHoEfPxmOl8zmq1Yjj0eOODe8T1HdLFXRLGDq9oFW5kysTSpYjr6RKiZiF0eiPCMCQIZ0yCEG/o4w6GjMdB9B5PQnx/xDu1OknNxDAL5PN5jLwZ2c9qWmR/rVxeK1wf9DyPnu2yWC5xhyPOuz280SRS1usP6Nt93q3toeZMrMoW1jpRs4hZsMjoJglJZUNMIkkyQufCpfP7UwajgMlsyeVFF9f1uApDbNvBGbjYQ5tarYYsKWQ1nYyWI5szyJsWhUKRTSPDdj5NPqch/P3cY+R7OK7PdDbHcQYMBkMCz8dxHGzHxR+53P3kQ1KqSkZRkEURKZkk/mqC229W+OG7r3j8/X2+uFtFeNax6V12sYdj3FHI8+4lnfNL/GAaHezZA7xBj88+/wi9UsHIZlFEEVmWiMU3eL9q8ctPBzSefMuXn76N4IxedG6+uIq66PT6zGcLlsDU8wnHAX7g896d2yiZDBWrhKoopNMp1FSKoqlz59Y2H+9ts1XUEX7+9Yx2u81ps0Wj2aZ19huNZotms8XpaYN2+4zWWZtqrYaayaAbRpTmOlVVVaMvmypVELM60r/F/gfhv2MgVFmiEAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 02&quot;
        title=&quot;Angular Jira Clone Tutorial Part 02&quot;
        src=&quot;/static/c4df26de62d7bd7ca870a32c75ee7ebe/d9199/05.png&quot;
        srcset=&quot;/static/c4df26de62d7bd7ca870a32c75ee7ebe/8ff5a/05.png 240w,
/static/c4df26de62d7bd7ca870a32c75ee7ebe/e85cb/05.png 480w,
/static/c4df26de62d7bd7ca870a32c75ee7ebe/d9199/05.png 960w,
/static/c4df26de62d7bd7ca870a32c75ee7ebe/6569d/05.png 1328w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;5-resizer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-resizer&quot; aria-label=&quot;5 resizer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Resizer&lt;/h3&gt;
&lt;p&gt;I have a dumb resizer component to have only an Input to toggle the arrow display.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ResizerComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; expanded&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expanded &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;chevron-left&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;chevron-right&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I also use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia&quot;&gt;window.matchMedia&lt;/a&gt; to automatically collapse the sidebar when the screen size is less than 1024px.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;handleResize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; match &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;(min-width: 1024px)&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  match&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;change&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expanded &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/d44fb5a5f102d505651ae3fdff23c57c/06.gif&quot; alt=&quot;Angular Jira Clone Tutorial Part 02&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;wrap-up-the-navigation-and-content&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wrap-up-the-navigation-and-content&quot; aria-label=&quot;wrap up the navigation and content permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Wrap up the navigation and content&lt;/h2&gt;
&lt;p&gt;After finishing the navigation component, I plugged it into the project page layout.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;w-full h-full flex&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-navigation&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;[expanded]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;expanded&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;(manualToggle)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;manualToggle()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-navigation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;router-outlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;router-outlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg-definitions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg-definitions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And I started to define the route for ProjectModule. All is set up!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; routes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Routes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProjectComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    children&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;board&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; BoardComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;settings&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SettingsComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;issue/:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;ProjectConst&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IssueId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        component&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FullIssueDetailComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        redirectTo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;board&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        pathMatch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;full&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Top-level await]]></title><description><![CDATA[How to use top-level await in ES Modules without all of the crazy hack]]></description><link>https://trungvose.comtop-level-await/</link><guid isPermaLink="false">https://trungvose.comtop-level-await/</guid><pubDate>Sun, 23 Aug 2020 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://v8.dev/features/top-level-await&quot;&gt;Top-level await&lt;/a&gt; enables developers to use the await keyword outside of async functions. It acts like a big async function causing other modules who import them to wait before they start evaluating their body.&lt;/p&gt;
&lt;h2 id=&quot;the-old-behavior&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-old-behavior&quot; aria-label=&quot;the old behavior permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The old behavior&lt;/h2&gt;
&lt;p&gt;When async/await was first introduced, attempting to use an await outside of an async function resulted in a SyntaxError.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;🎉&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → SyntaxError: await is only valid in async function&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Many developers utilized immediately-invoked async function expressions as a way to get access to the feature.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;🎉&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// → 🎉&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or also declare an async function and then call it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;doSomething&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;🎉&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// → 🎉&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-new-behavior&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-new-behavior&quot; aria-label=&quot;the new behavior permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The new behavior&lt;/h2&gt;
&lt;p&gt;With top-level await, the above code instead works the way you’d expect within modules:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;🎉&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → 🎉&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Top-level await only works at the &lt;strong&gt;top level of modules&lt;/strong&gt;. There is no support for classic scripts or non-async functions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 553px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/48db210b88fa6259ba9137cf1f0ec2ec/74cfa/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.083333333333332%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA1ElEQVQY023IQXKEIBCFYS8CIqgg4ygqNkzT4DiTVOX+J0ol5SKLfPUvXr1KCGGMKeUgyohUypFLgRCJ8vv9kXPJORNdpUQp0Xm+vAelVNU0jbU2IZ7nkyjFACXTIwZKtHsPELzft3UDCNM0z7MLIQKEbfNSqqpr204PDorzCfB5X/E2+dFFY+f+tnS90fpqGKwxQ99fW2tdKSXn+46PLxNRIzkIEaN/JJc+FzxS3NdxWEardd+23V9SykoqKYVoheCc8ZrXnIma1ZzxH0z8Ppyzf30Dc5A1vnygl3EAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Top level await&quot;
        title=&quot;Top level await&quot;
        src=&quot;/static/48db210b88fa6259ba9137cf1f0ec2ec/74cfa/01.png&quot;
        srcset=&quot;/static/48db210b88fa6259ba9137cf1f0ec2ec/8ff5a/01.png 240w,
/static/48db210b88fa6259ba9137cf1f0ec2ec/e85cb/01.png 480w,
/static/48db210b88fa6259ba9137cf1f0ec2ec/74cfa/01.png 553w&quot;
        sizes=&quot;(max-width: 553px) 100vw, 553px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;use-cases&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#use-cases&quot; aria-label=&quot;use cases permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Use cases&lt;/h2&gt;
&lt;h3 id=&quot;dynamic-dependency-pathing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dynamic-dependency-pathing&quot; aria-label=&quot;dynamic dependency pathing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dynamic dependency pathing&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; strings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/i18n/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;language&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This allows for modules to use runtime values in order to determine dependencies. This is useful for things like development/production splits, internationalization, environment splits, etc.&lt;/p&gt;
&lt;h3 id=&quot;resource-initialization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource-initialization&quot; aria-label=&quot;resource initialization permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resource initialization&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dbConnector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This allows modules to represent resources and also to produce errors in cases where the module cannot be used.&lt;/p&gt;
&lt;h3 id=&quot;dependency-fallbacks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dependency-fallbacks&quot; aria-label=&quot;dependency fallbacks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dependency fallbacks&lt;/h3&gt;
&lt;p&gt;The following example attempts to load a JavaScript library from CDN A, falling back to CDN B if that fails:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; jQuery
&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  jQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://cdn-a.example.com/jQuery&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  jQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://cdn-b.example.com/jQuery&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Space Invaders game built with Phaser 3 and TypeScript]]></title><description><![CDATA[There were quite a lot of changes between Phaser 2 and 3 but I like version 3 better. It made the code much more readable and structure]]></description><link>https://trungvose.comspace-invaders-phaser-3/</link><guid isPermaLink="false">https://trungvose.comspace-invaders-phaser-3/</guid><pubDate>Fri, 14 Aug 2020 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Another Space Invaders game built with Phaser 3 and TypeScript 😂😂😂&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It was based on &lt;a href=&quot;https://phaser.io/examples/v2/games/invaders&quot;&gt;Phaser 2 Invader official tutorial&lt;/a&gt; but written in Phaser 3 and TypeScript.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;working-game&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-game&quot; aria-label=&quot;working game permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Game&lt;/h2&gt;
&lt;p&gt;Check out the &lt;strong&gt;working game&lt;/strong&gt; -&gt; &lt;a href=&quot;https://invaders.trungk18.com&quot;&gt;https://invaders.trungk18.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The game has sounds, wear your 🎧 or turn on your 🔊 for a better experience.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/space-invaders-phaser-3/raw/master/dist/assets/readme/invaders-demo.gif&quot; alt=&quot;Space Invaders built with Phaser 3 and TypeScript&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/space-invaders-phaser-3&quot;&gt;https://github.com/trungvose/space-invaders-phaser-3&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;why&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why&quot; aria-label=&quot;why permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why?&lt;/h2&gt;
&lt;p&gt;I got a lot of motivation to work on some JS game after finishing &lt;a href=&quot;https://tetris.trungk18.com/&quot;&gt;Angular Tetris&lt;/a&gt;. I feel like gaming is a whole new world that I have never explored. For a simple Tetris game, probably I can leverage Angular with only simple state management and a game loop. But for complicated with a lot of animation stuff, I know I have to use a proper JS game framework.&lt;/p&gt;
&lt;p&gt;Phaser is one of the most famous ones. I spent only about an hour to walk through the &lt;a href=&quot;https://phaser.io/tutorials/making-your-first-phaser-3-game/part1&quot;&gt;official tutorial&lt;/a&gt; to build the first game. The experience was awesome and I started to look at their example. There were several examples only for Phaser 2 and one of them is &lt;a href=&quot;https://phaser.io/examples/v2/games/invaders&quot;&gt;Invaders&lt;/a&gt;. I really like how the game look and I decide to spend time going through the source code and migrate it over to Phaser 3 with TypeScript.&lt;/p&gt;
&lt;p&gt;In the end, what you are seeing is the result of that process. I did add some sound effects and make some changes to the UI. With only a few hundred lines of code, you could build a catchy looking game with Phaser 3. How cool is that?&lt;/p&gt;
&lt;h2 id=&quot;support&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#support&quot; aria-label=&quot;support permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Support&lt;/h2&gt;
&lt;p&gt;If you like my work, feel free to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ this repository. And we will be happy together :)&lt;/li&gt;
&lt;li&gt;&lt;a class=&quot;text-left&quot; title=&quot;Thanks for your support!&quot; href=&quot;https://www.buymeacoffee.com/trungvose&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://res.cloudinary.com/dvujyxh7e/image/upload/c_thumb,w_140,g_face/v1596378474/default-orange_uthxgz.jpg&quot; alt=&quot;Buy Me A Coffee&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks a bunch for stopping by and supporting me!&lt;/p&gt;
&lt;h2 id=&quot;time-spending&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#time-spending&quot; aria-label=&quot;time spending permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Time Spending&lt;/h2&gt;
&lt;p&gt;I only spent a couple of hours on one weekend to read the documentation and migrate the source code to Phaser 3. Based on the report, 8 hours was the total time spending. Monday, August 10 is a public holiday in Singapore so I have the right to finalize and publish the game 😎&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/space-invaders-phaser-3/raw/master/dist/assets/readme/time-spending.png&quot; alt=&quot;Space Invaders built with Phaser 3 and TypeScript&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;available-commands&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#available-commands&quot; aria-label=&quot;available commands permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Available Commands&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nodejs.org&quot;&gt;Node.js&lt;/a&gt; is required to install dependencies and run scripts via &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;npm i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install project dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;npm run watch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build the project and open web server running project, watching for changes. The web server should run on &lt;a href=&quot;http://localhost:4201&quot;&gt;http://localhost:4201&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;npm run dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds project and open web server, but do not watch for changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;npm run build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds code bundle with production settings (minification, no source maps, etc..)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;configuring-rollup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configuring-rollup&quot; aria-label=&quot;configuring rollup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Configuring Rollup&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Edit the file &lt;code class=&quot;language-text&quot;&gt;rollup.config.dev.js&lt;/code&gt; to edit the development build.&lt;/li&gt;
&lt;li&gt;Edit the file &lt;code class=&quot;language-text&quot;&gt;rollup.config.dist.js&lt;/code&gt; to edit the distribution build.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will find lots of comments inside the rollup config files to help you do this.&lt;/p&gt;
&lt;p&gt;Note that due to the build process involved, it can take around 20 seconds to build the initial bundle. Times will vary based on CPU and local drive speeds. The development config does not minify the code in order to save build time, but it does generate source maps. If you do not require these, disable them in the config to speed it up further.&lt;/p&gt;
&lt;h2 id=&quot;author-trung-vo-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#author-trung-vo-%EF%B8%8F&quot; aria-label=&quot;author trung vo ️ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Author: Trung Vo ✍️&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A young and passionate front-end engineer. Working with Angular and TypeScript. Like photography, running, cooking, and reading books.&lt;/li&gt;
&lt;li&gt;Author of &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;Angular Jira clone&lt;/a&gt; and &lt;a href=&quot;https://tetris.trungk18.com/&quot;&gt;Angular Tetris&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Personal blog: &lt;a href=&quot;https://trungk18.com/&quot;&gt;https://trungk18.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Say hello: trungk18 [et] gmail [dot] com&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;credits-and-references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#credits-and-references&quot; aria-label=&quot;credits and references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Credits and references&lt;/h2&gt;
&lt;p&gt;The below table listed all of the awesome resources that I have referenced to&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/photonstorm/phaser3-typescript-project-template&quot;&gt;@photonstorm/phaser3-typescript-project-template&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A quick-start project template combines Phaser 3 with &lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; and uses &lt;a href=&quot;https://rollupjs.org&quot;&gt;Rollup&lt;/a&gt; for bundling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://phaser.io/tutorials/making-your-first-phaser-3-game/part1&quot;&gt;Making Your First Phaser 3 Game&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Official guide to creating a small game involving a player running and jumping around platforms, collecting stars and avoiding baddies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://phaser.io/examples/v2/games/invaders&quot;&gt;Invaders Game - Phaser 2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Official tutorial to build Invaders game with Phaser 2. I reused its static resources but converting the code to Phaser 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://photonstorm.github.io/phaser3-docs/&quot;&gt;Phaser 3 API References&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;All of the API that you can refer to while working with Phaser 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/how-to-build-a-simple-game-in-the-browser-with-phaser-3-and-typescript-bdc94719135/&quot;&gt;Phaser 3 and TypeScript Tutorial&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;How to build a simple game in the browser with Phaser 3 and TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.classicgaming.cc/classics/space-invaders/sounds&quot;&gt;Space Invaders Sounds Effects&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Sound effects from the classic arcade game Space Invaders released in 1978 by Taito.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;upcoming&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#upcoming&quot; aria-label=&quot;upcoming permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Upcoming&lt;/h2&gt;
&lt;p&gt;I am having some ideas to start building some of my favorite games (and easy to build 🤣). It could be&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enhance the &lt;a href=&quot;https://tetris.trungk18.com/&quot;&gt;Angular Tetris&lt;/a&gt; with a few more games: racing, tank shotting.&lt;/li&gt;
&lt;li&gt;Duck Hunt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you would like to collaborate, tag &lt;a href=&quot;https://twitter.com/trungvose&quot;&gt;@trungvose&lt;/a&gt; on Twitter 👏👏👏&lt;/p&gt;
&lt;h2 id=&quot;contributing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contributing&quot; aria-label=&quot;contributing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contributing&lt;/h2&gt;
&lt;p&gt;If you have any ideas, just &lt;a href=&quot;https://github.com/trungvose/space-invaders-phaser-3/issues/new&quot;&gt;open an issue&lt;/a&gt; and tell me what you think. Notes that it is a very simple game, you could add in a lot of stuff: introducing level, add more invaders and stuff like that.&lt;/p&gt;
&lt;p&gt;If you’d like to contribute, please fork the repository and make changes as you’d like. &lt;a href=&quot;https://github.com/trungvose/space-invaders-phaser-3/compare&quot;&gt;Pull requests&lt;/a&gt; are warmly welcome.&lt;/p&gt;
&lt;h2 id=&quot;license&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#license&quot; aria-label=&quot;license permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;License&lt;/h2&gt;
&lt;p&gt;Feel free to use my code on your project. It would be great if you put a reference to this repository.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://opensource.org/licenses/MIT&quot;&gt;MIT&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Super Expressive - Easy Peasy Regex Generating]]></title><description><![CDATA[Struggling to write regex from scratch, checkout super-expressive, and a playground written by my friend @nartc]]></description><link>https://trungvose.comregex-playground-super-expressive/</link><guid isPermaLink="false">https://trungvose.comregex-playground-super-expressive/</guid><pubDate>Sun, 02 Aug 2020 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Regex is a very powerful tool, but its terse and cryptic vocabulary can make constructing and communicating them with others a challenge. Even developers who understand them well can have trouble reading their own back just a few months later! I am not good at Regex. During the seven years of my software engineer, I have never written a regex from scratch. I tried learning it a few times, seriously. But gradually, my regex memory is faded away.&lt;/p&gt;
&lt;h2 id=&quot;super-expressive&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#super-expressive&quot; aria-label=&quot;super expressive permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Super Expressive&lt;/h2&gt;
&lt;p&gt;Recently, I saw this awesome library &lt;a href=&quot;https://github.com/francisrstokes/super-expressive&quot;&gt;super-expressive&lt;/a&gt; and I immediately love it. It provides a programmatic and human-readable way to create regular expressions. Its API uses the fluent builder pattern and is completely immutable. It’s built to be discoverable and predictable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Properties and methods describe what they do in plain English&lt;/li&gt;
&lt;li&gt;Order matters! quantifiers are specified before the thing they change, just like in English (e.g. &lt;code class=&quot;language-text&quot;&gt;SuperExpressive().exactly(5).digit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;If you make a mistake, you’ll know how to fix it. SuperExpressive will guide you towards a fix if your expression is invalid&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/francisrstokes/super-expressive#subexpressionexpr-opts&quot;&gt;Sub-expressions&lt;/a&gt; can be used to create meaningful, reusable components&lt;/li&gt;
&lt;li&gt;Includes an &lt;code class=&quot;language-text&quot;&gt;index.d.ts&lt;/code&gt; file for full TypeScript support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SuperExpressive turns those complex and unweildy regexes that appear in code reviews into something that can be read, understood, and &lt;strong&gt;properly reviewed&lt;/strong&gt; by your peers - and maintained by anyone!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Quoted from the library README&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;installation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#installation&quot; aria-label=&quot;installation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Installation&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i super-expressive&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;usage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usage&quot; aria-label=&quot;usage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Usage&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; SuperExpressive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;super-expressive&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Or as an ES6 module&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; SuperExpressive &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;super-expressive&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The following example recognizes the HTTP protocol or some added string inside the list of URLs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; SuperExpressive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;super-expressive&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; httpRegex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SuperExpressive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;allowMultipleMatches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;anyOf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;nartc&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;jira&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toRegex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Produces the following regular expression:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;(?:http|nartc|jira)&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can immediately use the &lt;code class=&quot;language-text&quot;&gt;httpRegex&lt;/code&gt; variable to test against your input string and that’s it. The code is much more readable and maintainable.&lt;/p&gt;
&lt;p&gt;It is just a simple example, usually regex will be much more complex. You can check the &lt;a href=&quot;https://github.com/francisrstokes/super-expressive#API&quot;&gt;full list of the API&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;playground&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#playground&quot; aria-label=&quot;playground permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Playground&lt;/h2&gt;
&lt;p&gt;If you need a regex for validation purposes on Angular and you don’t want to install &lt;a href=&quot;https://github.com/francisrstokes/super-expressive&quot;&gt;super-expressive&lt;/a&gt;, that’s fine. My friend &lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt; built a playground online where you can simply generate your regex using &lt;a href=&quot;https://github.com/francisrstokes/super-expressive&quot;&gt;super-expressive&lt;/a&gt; syntax and test your result as well.&lt;/p&gt;
&lt;p&gt;Check out the working app -&gt; &lt;a href=&quot;https://sepg.netlify.app/&quot;&gt;https://sepg.netlify.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/nartc/super-expressive-playground/raw/master/src/assets/images/super-expressive-demo.gif&quot; alt=&quot;SuperExpressive Playground by Nartc&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;I contributed to the responsiveness of the application on the mobile web browser.&lt;/p&gt;
&lt;p&gt;If you like it, &lt;a href=&quot;https://github.com/nartc/super-expressive-playground&quot;&gt;give a star&lt;/a&gt; to my friend repo yeah! 💪💪💪&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A childhood memory Tetris game built with Angular 10 and Akita]]></title><description><![CDATA[I made a retro Tetris game with Angular. The first game that I have ever built :)]]></description><link>https://trungvose.comangular-tetris/</link><guid isPermaLink="false">https://trungvose.comangular-tetris/</guid><pubDate>Sat, 25 Jul 2020 16:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;working-game&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-game&quot; aria-label=&quot;working game permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Game&lt;/h2&gt;
&lt;p&gt;Check out the &lt;strong&gt;working game&lt;/strong&gt; -&gt; &lt;a href=&quot;https://tetris.trungk18.com&quot;&gt;https://tetris.trungk18.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The game has sounds, wear your 🎧 or turn on your 🔊 for a better experience.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/angular-tetris-demo.gif&quot; alt=&quot;A childhood memory Tetris game built with Angular 10 and Akita&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you like my work, feel free to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;text-left&quot; title=&quot;Thanks for your support!&quot; href=&quot;https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2Ftrungk18%2Fangular-tetris&amp;text=Awesome%20Tetris%20game%20built%20with%20Angular%2010%20and%20Akita%2C%20can%20you%20get%20999999%20points%3F&amp;hashtags=angular,angulartetris,akita,typescript&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;icon-twitter&quot;&gt;&lt;/span&gt;Tweet&lt;/a&gt; about Angular Tetris&lt;/li&gt;
&lt;li&gt;⭐ this repository. And we will be happy together :)&lt;/li&gt;
&lt;li&gt;&lt;a class=&quot;text-left&quot; title=&quot;Thanks for your support!&quot; href=&quot;https://www.buymeacoffee.com/trungvose&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://res.cloudinary.com/dvujyxh7e/image/upload/c_thumb,w_140,g_face/v1596378474/default-orange_uthxgz.jpg&quot; alt=&quot;Buy Me A Coffee&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks a bunch for stopping by and supporting me!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The mobile device support is very limited at the moment - can play but tapping multiple times will be an issue, no button press effect. I will spend the next few days working on it. The screenshot below recorded on iPhone X.
Please tweet and tag me for any issues that you are currently facing!!&lt;/p&gt;
&lt;p&gt;Thanks for your understanding. Stay tuned!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/angular-tetris-iphonex.gif&quot; alt=&quot;A childhood memory Tetris game built with Angular 10 and Akita&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;why&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why&quot; aria-label=&quot;why permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why?&lt;/h2&gt;
&lt;p&gt;Tetris was the first game that my dad bought for me and It cost about 1$ US at that time. It didn’t sound a lot today. But 20 years ago, 1$ can feed my family for at least a few days. Put it that way, with 1$ you can buy two dozens eggs.
This is the only gaming “machine” that I ever had until my first computer arrived. I have never had a SNES or PS1 at home.&lt;/p&gt;
&lt;p&gt;My Tetris was exactly in the same yellow color and it was so big, running on 2 AA battery. It is how it looks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/retro-tetris.jpg&quot; alt=&quot;Retro Tetris&quot;&gt;&lt;/p&gt;
&lt;p&gt;After showing my wife the &lt;a href=&quot;https://github.com/Binaryify/vue-tetris&quot;&gt;Tetris game built with Vue&lt;/a&gt;. She asked me why I didn’t build the same &lt;u&gt;Tetris with Angular&lt;/u&gt;? And here you go.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The game can hold up to a maximum score of 999999 (one million minus one 😂) and I have never reached that very end.&lt;/p&gt;
&lt;p&gt;Please &lt;a href=&quot;https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2Ftrungk18%2Fangular-tetris&amp;#x26;text=Woo-hoo!%20I%20got%20a%20999999%20points%20on%20Angular%20Tetris%20%40trungvose.%20Wanna%20join%20the%20party%3F%20&amp;#x26;hashtags=angular,angulartetris,akita,typescript&quot;&gt;tweet&lt;/a&gt; the screenshot with your highest score, together with hashtag &lt;code class=&quot;language-text&quot;&gt;#angulartetris&lt;/code&gt; and my name tagged as well &lt;code class=&quot;language-text&quot;&gt;@trungvose&lt;/code&gt;. I will send &lt;strong&gt;a free gift&lt;/strong&gt; to the one having the highest score of the day, from now till &lt;u&gt;1 Aug 2020&lt;/u&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;who-is-this-for&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#who-is-this-for&quot; aria-label=&quot;who is this for permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Who is this for?&lt;/h2&gt;
&lt;p&gt;I built this game dedicated to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For anyone that grew up with Tetris as a part of your memory. It was my childhood memory and I hope you enjoy the game as well.&lt;/li&gt;
&lt;li&gt;For the Angular developer community, I have never really seen a game that built with Angular and that’s my answer. Using Akita as the underlying state management helps me to see all of the data flow, it is great for debugging. I wanted to see more Angular game from you guys 💪💪💪&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-play&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-play&quot; aria-label=&quot;how to play permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to play&lt;/h2&gt;
&lt;h3 id=&quot;before-playing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#before-playing&quot; aria-label=&quot;before playing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Before playing&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You can use both keyboard and mouse to play. But prefer to use &lt;u&gt;keyboard&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Press arrow left and right to change the speed of the game &lt;strong&gt;(1 - 6)&lt;/strong&gt;. The higher the number, the faster the piece will fall&lt;/li&gt;
&lt;li&gt;Press arrow up and down to change how many of lines have been filled before starting the game &lt;strong&gt;(1 - 10)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;Space&lt;/code&gt; to start the game&lt;/li&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;P&lt;/code&gt; for pause/resume game&lt;/li&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;R&lt;/code&gt; for resetting the game&lt;/li&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;S&lt;/code&gt; for the turn on/off the sounds&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;playing-game&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#playing-game&quot; aria-label=&quot;playing game permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Playing game&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;Space&lt;/code&gt; make the piece drop quickly&lt;/li&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;Arrow left&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;right&lt;/code&gt; for moving left and right&lt;/li&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;Arrow up&lt;/code&gt; to rotate the piece&lt;/li&gt;
&lt;li&gt;Press &lt;code class=&quot;language-text&quot;&gt;Arrow down&lt;/code&gt; to move a piece faster&lt;/li&gt;
&lt;li&gt;When clearing lines, you will receive a point - 100 points for 1 line, 300 points for 2 lines, 700 points for 3 lines, 1500 points for 4 lines&lt;/li&gt;
&lt;li&gt;The drop speed of the pieces increases with the number of rows eliminated (one level up for every 20 lines cleared)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;techstack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#techstack&quot; aria-label=&quot;techstack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Techstack&lt;/h2&gt;
&lt;p&gt;I built it barely with Angular and Akita, no additional UI framework/library was required.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/tech-stack.png&quot; alt=&quot;Angular Tetris&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;development-challenge&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#development-challenge&quot; aria-label=&quot;development challenge permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Development Challenge&lt;/h2&gt;
&lt;p&gt;I got the inspiration from the same but different &lt;a href=&quot;https://github.com/Binaryify/vue-tetris&quot;&gt;Tetris game built with Vue&lt;/a&gt;. To not reinvented the wheel, I started to look at Vue code and thought it would be very identical to Angular. But later one, I realized a few catches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Vue source code was written a few years ago with pure JS. I could find several problems that the compiler didn’t tell you. Such as giving &lt;code class=&quot;language-text&quot;&gt;parseInt&lt;/code&gt; a number. It is still working though, but I don’t like it.&lt;/li&gt;
&lt;li&gt;There was extensive use of &lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;setInterval&lt;/code&gt; for making animations. I rewrote all of the animation logic using RxJS. You will see the detail below.&lt;/li&gt;
&lt;li&gt;The brain of the game also used &lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt; for the game loop. It was not a problem, but I was having a &lt;u&gt;hard time&lt;/u&gt; understanding the code on some essential elements: how to render the piece to the UI, how the calculation makes sense with XY axis. In the end, I changed all of the logic to a proper OOP way using TypeScript class, based on &lt;a href=&quot;https://github.com/chrum/ngx-tetris&quot;&gt;@chrum/ngx-tetris&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tetris-core&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tetris-core&quot; aria-label=&quot;tetris core permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tetris Core&lt;/h3&gt;
&lt;p&gt;It is the most important part of the game. As I am following the Vue source code, It is getting harder to understand what was the developer’s intention. The Vue version inspired me but I think I have to write the core Tetris differently.&lt;/p&gt;
&lt;p&gt;Take a look at the two blocks of code below which do the same rendering piece on the screen and you will understand what I am talking about. The left side was rewritten with Angular and TypeScript and the right side was the JS version.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/compare01.png&quot; alt=&quot;Angular Tetris&quot;&gt;&lt;/p&gt;
&lt;p&gt;I always think that your code must be written as you talk to people, without explaining a word. Otherwise, when someone comes in and reads your code and maintains it, they will be struggling.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“ Code is like humor. When you have to explain it, it’s bad.” – Cory House&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And let me emphasize it again, I didn’t write the brain of the game from scratch. I adapted the well-written source by &lt;a href=&quot;https://github.com/chrum/ngx-tetris&quot;&gt;@chrum/ngx-tetris&lt;/a&gt; for Tetris core. I did refactor some parts to support Akita and wrote some new functionality as well.&lt;/p&gt;
&lt;h3 id=&quot;akita-state-management--dev-tool-support&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#akita-state-management--dev-tool-support&quot; aria-label=&quot;akita state management  dev tool support permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Akita state management + Dev tool support&lt;/h3&gt;
&lt;p&gt;Although you don’t dispatch any action, Akita will still do it undo the hood as the Update action. And you still can see the data with &lt;a href=&quot;https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en&quot;&gt;Redux DevTools&lt;/a&gt;. Remember to put that option into your &lt;code class=&quot;language-text&quot;&gt;AppModule&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;imports&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;environment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;production &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AkitaNgDevtools&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I turn it on all the time on &lt;a href=&quot;https://tetris.trungk18.com&quot;&gt;tetris.trungk18.com&lt;/a&gt;, you can open the DevTools and start seeing the data flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/akita-devtool.gif&quot; alt=&quot;Angular Tetris&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: opening the DevTools could reduce the performance of the game significantly. I recommended you turn it off when you want to archive a high score 🤓&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;customizing-piece&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#customizing-piece&quot; aria-label=&quot;customizing piece permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Customizing Piece&lt;/h3&gt;
&lt;p&gt;I defined a base &lt;a href=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/app/interface/piece/piece.ts&quot;&gt;Piece class&lt;/a&gt; for a piece. And for each type of piece, it will extend from the same base class to inherit the same capability&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Piece&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
  y&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
  rotation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; PieceRotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Deg0
  type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PieceTypes
  shape&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Shape
  next&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Shape

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _shapes&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Shapes
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _lastConfig&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Partial&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Piece&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; y
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Piece &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_lastConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      y&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      rotation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rotation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      shape&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shape&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_newPiece&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For example, I have a piece L. I create a new class name &lt;a href=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/app/interface/piece/L.ts&quot;&gt;PieceL&lt;/a&gt;. I will contain the shape of L in four different rotation so that I don’t have to mess up with the math to do minus plus on the XY axis. And I think defining in that way makes the code self-express better. If you see 1, it means on the matrix it will be filled, 0 mean empty tile.&lt;/p&gt;
&lt;p&gt;If my team member needs to maintain the code, I hope he will understand what I was trying to write immediately. Or maybe not 🤣&lt;/p&gt;
&lt;p&gt;One import property of the Piece is the &lt;code class=&quot;language-text&quot;&gt;next&lt;/code&gt; property to display the piece shape on the decoration box for the upcoming piece.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ShapesL&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Shapes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ShapesL&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;PieceRotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Deg0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

ShapesL&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;PieceRotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Deg90&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Piece&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; PieceTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setShapes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ShapesL&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now is the interesting part, you create a custom piece by yourself. Simply create a new class that extends from &lt;code class=&quot;language-text&quot;&gt;Piece&lt;/code&gt; with different rotations.&lt;/p&gt;
&lt;p&gt;For instance, I will define a new piece call F with class name &lt;a href=&quot;https://github.com/trungvose/angular-tetris/blob/feature/pieceF/https://github.com/trungvose/angular-tetris/raw/master/src/app/interface/piece/F.ts&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;PieceF&lt;/code&gt;&lt;/a&gt;. That is how it should look like.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ShapesF&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Shapes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
ShapesF&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;PieceRotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Deg0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceF&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Piece&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; PieceTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;F&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;next &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setShapes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ShapesF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the last step, go to &lt;a href=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/app/factory/piece-factory.ts&quot;&gt;PieceFactory&lt;/a&gt; to add the new PieceF into the available pieces.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceFactory&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _available&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; Piece&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_available&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;PieceF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And you’re all set, this is the result. See how easy it is to understand the code and add a custom piece that you like.&lt;/p&gt;
&lt;p&gt;The source code for that custom piece F, you can see at &lt;a href=&quot;https://github.com/trungvose/angular-tetris/tree/feature/pieceF&quot;&gt;feature/pieceF&lt;/a&gt; branch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/piecef-demo.gif&quot; alt=&quot;Angular Tetris Piece F&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;animation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#animation&quot; aria-label=&quot;animation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Animation&lt;/h3&gt;
&lt;p&gt;I rewrote the animation with RxJS. See the comparison below for the simple dinosaurs running animation at the beginning of the game.&lt;/p&gt;
&lt;p&gt;You could do a lot of stuff if you know RxJS well enough :) I think I need to strengthen my RxJS knowledge soon enough as well. Super powerful.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/compare02.png&quot; alt=&quot;Angular Tetris&quot;&gt;&lt;/p&gt;
&lt;p&gt;The actual result doesn’t look very identical but it is good enough in my standard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/compare02-result.gif&quot; alt=&quot;Angular Tetris&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;web-audio-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#web-audio-api&quot; aria-label=&quot;web audio api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web Audio API&lt;/h3&gt;
&lt;p&gt;There are many sound effects in the game such as when you press space, or left, right. In reality, all of the sounds were a reference to a single file &lt;a href=&quot;https://github.com/trungvose/angular-tetris/blob/master/src/assets/tetris-sound.mp3&quot;&gt;assets/tetris-sound.mp3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I don’t have much experience working with audio before but the Web Audio API looks very promising. You could do more with it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;See the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API&quot;&gt;official documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;See how I load the mp3 file and store it in &lt;a href=&quot;https://github.com/trungvose/angular-tetris/blob/master/src/app/services/sound-manager.service.ts&quot;&gt;sound-manager.service.ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_delivery/Web_Audio_API_cross_browser&quot;&gt;Writing Web Audio API code that works in every browser&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;keyboard-handling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keyboard-handling&quot; aria-label=&quot;keyboard handling permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Keyboard handling&lt;/h3&gt;
&lt;p&gt;I planned to use &lt;a href=&quot;https://github.com/ngneat/hotkeys&quot;&gt;@ngneat/hotkeys&lt;/a&gt; but I decided to use &lt;code class=&quot;language-text&quot;&gt;@HostListener&lt;/code&gt; instead. A simple implementation could look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HostListener&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;KeyDown&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;TetrisKeyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Left&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;keyDownLeft&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_soundManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_keyboardService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setKeỵ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    left&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hasCurrent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tetrisService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;moveLeft&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_tetrisService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decreaseLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See more at &lt;a href=&quot;https://github.com/trungvose/angular-tetris/blob/master/src/app/containers/angular-tetris/angular-tetris.component.ts&quot;&gt;containers/angular-tetris/angular-tetris.component.ts&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;features-and-roadmap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#features-and-roadmap&quot; aria-label=&quot;features and roadmap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Features and Roadmap&lt;/h2&gt;
&lt;h3 id=&quot;phase-1---angular-tetris-basic-functionality&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#phase-1---angular-tetris-basic-functionality&quot; aria-label=&quot;phase 1   angular tetris basic functionality permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Phase 1 - Angular Tetris basic functionality&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;July 10 - 23, 2020&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Proven, scalable, and easy to understand project structure&lt;/li&gt;
&lt;li&gt;Basic Tetris functionality&lt;/li&gt;
&lt;li&gt;Six levels&lt;/li&gt;
&lt;li&gt;Local storage high score&lt;/li&gt;
&lt;li&gt;Sounds effects&lt;/li&gt;
&lt;li&gt;Limited mobile support&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;phase-2---firebase-high-score-service-worker-more-sounds-effect-more-animation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#phase-2---firebase-high-score-service-worker-more-sounds-effect-more-animation&quot; aria-label=&quot;phase 2   firebase high score service worker more sounds effect more animation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Phase 2 - Firebase high score, service worker, more sounds effect, more animation&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;TBD&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Fully mobile support&lt;/li&gt;
&lt;li&gt;Offline mode (play without internet connection)&lt;/li&gt;
&lt;li&gt;Firebase high score&lt;/li&gt;
&lt;li&gt;More sound effects&lt;/li&gt;
&lt;li&gt;More animations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;time-spending&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#time-spending&quot; aria-label=&quot;time spending permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Time spending&lt;/h2&gt;
&lt;p&gt;I was still working with &lt;a href=&quot;https://github.com/nartc&quot;&gt;Chau Tran&lt;/a&gt; on phase two of &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;Angular Jira clone&lt;/a&gt; when I saw that Tetris game built with Vue. My wife wanted to have a version that I built so that I decided to finish the Angular Tetris first before completing Jira clone phase two.&lt;/p&gt;
&lt;p&gt;According to waka time report, I have spent about 30 hours working on this project. Which is equal to &lt;a href=&quot;https://www.strava.com/activities/2902245728&quot;&gt;run a marathon five times&lt;/a&gt; at my current speed 😩&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/angular-tetris/raw/master/src/assets/readme/time-spending.png&quot; alt=&quot;Angular Tetris&quot;&gt;&lt;/p&gt;
&lt;p&gt;The flow was easy. I designed a simple &lt;a href=&quot;https://trungvose.notion.site/Phase-1-efe23d2a3f3946e2a5c7fda1ccff3a1c&quot;&gt;to do list&lt;/a&gt;, then start reading the code in Vue. And start working on the Angular at the same time. Halfway, I start to read &lt;a href=&quot;https://github.com/chrum/ngx-tetris&quot;&gt;@chrum/ngx-tetris&lt;/a&gt; instead of the Vue source. I kept coding until I have the final result. 30 hours was a good number. It would take me longer, or lesser. But I enjoyed the experience working on the first-ever game I have built.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-development-environment-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-up-development-environment-&quot; aria-label=&quot;setting up development environment  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting up development environment 🛠&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;git clone https://github.com/trungvose/angular-tetris.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cd angular-tetris&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The app should run on &lt;code class=&quot;language-text&quot;&gt;http://localhost:4200/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;author-trung-vo-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#author-trung-vo-%EF%B8%8F&quot; aria-label=&quot;author trung vo ️ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Author: Trung Vo ✍️&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A young and passionate front-end engineer. Working with Angular and TypeScript. Like photography, running, cooking, and reading books.&lt;/li&gt;
&lt;li&gt;Author of Angular Jira clone -&gt; &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;jira.trungk18.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Personal blog: &lt;a href=&quot;https://trungk18.com/&quot;&gt;https://trungk18.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Say hello: trungk18 [et] gmail [dot] com&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;credits-and-references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#credits-and-references&quot; aria-label=&quot;credits and references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Credits and references&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/Binaryify/vue-tetris&quot;&gt;@Binaryify/vue-tetris&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Vue Tetris, I reused part of HTML, CSS and static assets from that project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/chrum/ngx-tetris&quot;&gt;@chrum/ngx-tetris&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A comprehensive core Tetris written with Angular, I reused part of that for the brain of the game.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://medium.com/angular-in-depth/game-development-tetris-in-angular-64ef96ce56f7&quot;&gt;Game Development: Tetris in Angular&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A detailed excellent article about how to build a complete Tetris game. I didn’t check the code but I learned much more from that&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://tetris.fandom.com/wiki/SRS&quot;&gt;Super Rotation System&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A standard for how the piece behaves. I didn’t follow everything but it is good to know as wells&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;contributing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contributing&quot; aria-label=&quot;contributing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contributing&lt;/h2&gt;
&lt;p&gt;If you have any ideas, just &lt;a href=&quot;https://github.com/trungvose/angular-tetris/issues/new/choose&quot;&gt;open an issue&lt;/a&gt; and tell me what you think.&lt;/p&gt;
&lt;p&gt;If you’d like to contribute, please fork the repository and make changes as you’d like. &lt;a href=&quot;https://github.com/trungvose/angular-tetris/pulls&quot;&gt;Pull requests&lt;/a&gt; are warmly welcome.&lt;/p&gt;
&lt;h2 id=&quot;license&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#license&quot; aria-label=&quot;license permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;License&lt;/h2&gt;
&lt;p&gt;Feel free to use my code on your project. It would be great if you put a reference to this repository.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://opensource.org/licenses/MIT&quot;&gt;MIT&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 01 - Create a new repository and set up a new Angular application with CLI]]></title><description><![CDATA[I spent my spare time to build a cloned Jira app with Angular 9, Akita and ng-zorro in about two weeks, including the weekend]]></description><link>https://trungvose.comangular-jira-clone-tutorial-01-planning-and-set-up/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-01-planning-and-set-up/</guid><pubDate>Mon, 29 Jun 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have received an overwhelming response for #jiraclone application. Thank you guys for your support. I appreciate that you spend your time visiting my work, and it means a lot to me.&lt;/p&gt;
&lt;p&gt;So as I promised, this post will be the first tutorial on how I built #jiraclone.&lt;/p&gt;
&lt;p&gt;In case you don’t know what that is, check it out - &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira.trungk18.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://trungvose.com/tags/jira-clone/&quot;&gt;all tutorials for Jira clone&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;before-starting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#before-starting&quot; aria-label=&quot;before starting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Before starting&lt;/h2&gt;
&lt;p&gt;When I look at the AJC, it is not a big one, but not a small one either. I started to break the big task into &lt;a href=&quot;https://trungvose.notion.site/Angular-Jira-clone-Phase-1-79d3205e26024357a75ebfc00aba558e&quot;&gt;a simple to-do list on notion&lt;/a&gt;. Once I know what needs to be done, what I need is to follow the plan. That’s my approach.&lt;/p&gt;
&lt;p&gt;So the list contains the three big items with some major works that need to be done. For each small task, I can also break them into smaller tasks such as the UI and the handling part with Angular.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Groundwork:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a repository on Github&lt;/li&gt;
&lt;li&gt;Set up project structure with CLI&lt;/li&gt;
&lt;li&gt;Configure TailwindCSS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Backend&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create interface and sample JSON data&lt;/li&gt;
&lt;li&gt;Add the simple API with NestJS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Frontend&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Layout: includes left nav, sidebar, resizer and the like&lt;/li&gt;
&lt;li&gt;Breadcrumb&lt;/li&gt;
&lt;li&gt;Drag and drop kanban board&lt;/li&gt;
&lt;li&gt;Filtering&lt;/li&gt;
&lt;li&gt;Issue detail view, modal&lt;/li&gt;
&lt;li&gt;Inline edit issue&lt;/li&gt;
&lt;li&gt;Add new issue modal&lt;/li&gt;
&lt;li&gt;Search issue&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a shorter list than the one I listed above on notion. For the full list, please visit &lt;a href=&quot;https://trungvose.notion.site/Angular-Jira-clone-Phase-1-79d3205e26024357a75ebfc00aba558e&quot;&gt;notion&lt;/a&gt;. I want to give you some ideas on what needs to be done to bring the application to life.&lt;/p&gt;
&lt;h2 id=&quot;1-create-a-new-repository-on-github&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-create-a-new-repository-on-github&quot; aria-label=&quot;1 create a new repository on github permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Create a new repository on Github&lt;/h2&gt;
&lt;p&gt;That’s what I usually did. I don’t generate the Angular code with CLI, then push it to Github remote. I did the other way round.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new repository&lt;/li&gt;
&lt;li&gt;Clone to my local machine&lt;/li&gt;
&lt;li&gt;Open the folder, and start to create Angular application.&lt;/li&gt;
&lt;li&gt;And push&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let get started&lt;/p&gt;
&lt;h3 id=&quot;why-you-should-use-source-control&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-you-should-use-source-control&quot; aria-label=&quot;why you should use source control permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why you should use source-control?&lt;/h3&gt;
&lt;p&gt;It might not be Github, it could be Gitlab or Bitbucket. From my personal experience, using source control is &lt;u&gt;A MUST&lt;/u&gt;. You can’t just write your code and do Ctrl + Z to undo it. Many times I make something that works and, then I broke it. Without a source-control, you have no way to go back to the previous state of your code.&lt;/p&gt;
&lt;h3 id=&quot;open-httpsgithubcomnew-and-create-a-new-repository&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#open-httpsgithubcomnew-and-create-a-new-repository&quot; aria-label=&quot;open httpsgithubcomnew and create a new repository permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Open &lt;a href=&quot;https://github.com/new&quot;&gt;https://github.com/new&lt;/a&gt; and create a new repository&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 730px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4cef216ab0f39f5c2160110b36009ca8/e9beb/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACDUlEQVQ4y41UPY/UMBDN74eKigYaGhok+pNoaU5CokQUNGw26484juOvxM5DM2Fvd2Hvdi09jT1JXubNjKeRUqJtW2il0HUHtO0eu13Lvpxn1FpRSrmJZSn8bqONw0EaSG3RG4thsLCWMMK5Cd57eB8QCCFgmjzbo5/PbCcsy4ImzoDPFWMoyMuKa4u867rehSZFD6BeEFDo9EeKJMbIZ6KlD3CDvGnlAKEMS+zNgMkHlFohlYYQCgchEGICcZW6Ii/5uoq/P2sGF9EJBSkV9t2BiSh3wzBC94bP2gwYR4fRORhrOH/jZBmTd5y7I2nTa81kVIxjYnPOXGHCPM9IOSOldObPCNEjpICYIlf5idBT5aiCMSGmjFIqYzkD+2p9erad1ycspWBeCvubuq4bAfUR7Wu9CSK4BnrWUBWV7tH3PcsjOZs97f/FsQBbQ12uxscZzlkYMyCnvCFvOOWN9if/iRBXCNOCMA3Q2nDf0QcUdYqJyXmf0n8t8mwf5pyQSWLKXE0iCDEipoQQIlf+GNVdN4Vy13Udt45UCkIIbmYaEkJIjprydu9qrJuw6w44KI3feyLWnE+lNhtjwuQmtudynyXMAYhjRbALvC2YC1i2GWj6GAiptulzNjxelPzw6ys+/XjA559fGC557gYaCPfm7YLw7fePeP34Hm++fcCrx3cw0V5cdrwg75rkP4Kvew8TjFC5AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/4cef216ab0f39f5c2160110b36009ca8/e9beb/01.png&quot;
        srcset=&quot;/static/4cef216ab0f39f5c2160110b36009ca8/8ff5a/01.png 240w,
/static/4cef216ab0f39f5c2160110b36009ca8/e85cb/01.png 480w,
/static/4cef216ab0f39f5c2160110b36009ca8/e9beb/01.png 730w&quot;
        sizes=&quot;(max-width: 730px) 100vw, 730px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There is a little information that you need to input.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repository name; &lt;code class=&quot;language-text&quot;&gt;jira-angular-clone&lt;/code&gt; or whatever name you want.&lt;/li&gt;
&lt;li&gt;Description: I initially put “A simplified Jira clone built with Angular 9 and Akita” but it is optional anyway.&lt;/li&gt;
&lt;li&gt;I choose the private repository because I only want to publish it once I have something :“)&lt;/li&gt;
&lt;li&gt;You can select to init with a default README file. It is a normal Markdown file, but if you are not familiar with Github and Markdown. I recommended you check this box.&lt;/li&gt;
&lt;li&gt;For the &lt;code class=&quot;language-text&quot;&gt;.gitignore&lt;/code&gt; option, select &lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;. You don’t want to commit the whole &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; to your repo.&lt;/li&gt;
&lt;li&gt;And finally, add the license. Usually, I choose MIT because all of my code is open to the public.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s all.&lt;/p&gt;
&lt;p&gt;Now you can start to bring that to your machine.&lt;/p&gt;
&lt;p&gt;Simply do &lt;code class=&quot;language-text&quot;&gt;git clone your_repo_url&lt;/code&gt; and replace the &lt;code class=&quot;language-text&quot;&gt;your_repo_url&lt;/code&gt; with the link from the screenshot below.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 404px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9aa11a921c9b879099b13bb18b6bc28c/494f9/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACBElEQVQoz3VSu4oUQRTtDzLzG4wMFvwEswVB8AcWQQwNRAQTBRMxMDPQTCNDE1dxdUZne2b6/aiu7urueh25t3pW0bXgcB917qPurch7jwP+PZf4LuX9PhEnWwytDTopMQwKreigjQ2+TqJpBWTfwzvg2cdXuP3mHu6+f4STdw9x5+19PPjwlJuKQlEP5xwGNWL98xybTYxv39cYxglqnLBa/8DXsxU25zGMdjh+fYIrj6/j2vObuPHyFq4+OcLRi2M47xBNxmPWlhP+fbTWMNZe2M57GGMhRom0K7DvcuxEhp0o0CgB7zyianAYZoskSRDHW6RphtV6jSRJWcbbHT/3f6fugVKGcVOxqOwthsmgbVvskxR5USDNMmRZjrppWGZ5ASnlMssWTdMwn3RtHWgL2hgYYxD1o8E4awxKcUDfDwxKRrboOnRdx7JpWva3QlxgnmceC3XHCb1zwbAWRVlycFXVKMoKQnSo64Y7oU7LsmKdClV1jXnWyMsaVdPC2iWhUiOmaV42bQOshV90Ilobilr2Owbdee+Ya//skJYgZQ9jHc9SLaBRkE0/gMjaBEm+Aw4cip1mHRLS7PpeccDpTuEsHbHKRpxuFT5tFXIxM5ECJm347ste4fMu3MfVxOMap8CLaFtV3fCnlrSQQYXFDAqS0KugL/IA4hKUCjrF09N/AYDqkh+cz19qAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/9aa11a921c9b879099b13bb18b6bc28c/494f9/02.png&quot;
        srcset=&quot;/static/9aa11a921c9b879099b13bb18b6bc28c/8ff5a/02.png 240w,
/static/9aa11a921c9b879099b13bb18b6bc28c/494f9/02.png 404w&quot;
        sizes=&quot;(max-width: 404px) 100vw, 404px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-create-a-new-angular-application-with-cli&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-create-a-new-angular-application-with-cli&quot; aria-label=&quot;2 create a new angular application with cli permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Create a new Angular application with CLI&lt;/h2&gt;
&lt;h3 id=&quot;install-angular-cli-if-you-dont-have-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#install-angular-cli-if-you-dont-have-it&quot; aria-label=&quot;install angular cli if you dont have it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Install Angular CLI if you don’t have it&lt;/h3&gt;
&lt;p&gt;If you don’t know what Angular CLI is. Read &lt;a href=&quot;https://cli.angular.io/&quot;&gt;more&lt;/a&gt;. It is the command-line interface for Angular, which means you can open CMD and type a command. CLI will do &lt;u&gt;something&lt;/u&gt; for you. See the screenshot below from CLI website.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 759px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c2606139314549aeaf738ab7d0bef367/ef3e1/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABl0lEQVQY003OP28SYQDHcd6DphrkeeCOHhwcV+7Pcxx3wF3TVBMdNFEH4+ILUAeNcfKdGJN2r1o3ozU6GOvgatIBCwGKUXttSqzr14CLwyff7Zdf7klLYcWKsBsT9RLavR7RXJIs6nU6XI4jviqX/cBlfy1lcGmNwfrqwrfQo+836fsOfeWQu2vonJUa5rLBcllH13TqZg2jXKYoBGeWlmjm8+wFDqO2z/j6NaZ3bnNw6yaTG1cZrScM44Bhp8Ww7ZN7UK8iqiZOo4HdsHEcB6tuUSoWKUnJBVkk1DX6ocs09hkpm7FvMwldJqGz6EEaMb1ykUkS/hs8b1SwLQuzVsMwKggpKQiBlJJzQqC0EoPI5UcvYLb5jNOdN8y2nzN79YI/H96RPbzH1DX53lXkHltV8iUNyzAoz18VCuhSLpSlRAhBrGuMOx5ZGjDbeMrvt685ebnFyfYWp+93OH50n5+tBr/SFrm9rsfHcIXd9lyTz5HD7n8+tZt8iRyyVHGcKrKgxqFXJVM1MmVy6FXI4hWOVgOOUsVfNtD3LgzgDTcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/c2606139314549aeaf738ab7d0bef367/ef3e1/03.png&quot;
        srcset=&quot;/static/c2606139314549aeaf738ab7d0bef367/8ff5a/03.png 240w,
/static/c2606139314549aeaf738ab7d0bef367/e85cb/03.png 480w,
/static/c2606139314549aeaf738ab7d0bef367/ef3e1/03.png 759w&quot;
        sizes=&quot;(max-width: 759px) 100vw, 759px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If you don’t have it installed. Open CMD, and simple type &lt;code class=&quot;language-text&quot;&gt;npm install -g @angular/cli&lt;/code&gt; to install it globally on your local machine.&lt;/p&gt;
&lt;p&gt;Once you have it install, verify the version by running the command &lt;code class=&quot;language-text&quot;&gt;ng --version&lt;/code&gt;. It should like that.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 476px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/60eefaea18cf7b3982e52de61b59a651/f2205/04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 97.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACXElEQVQ4y5VU2XajMAzlMzKNLRkIDfuaBAiETtO0ne3/f+fOsSEpnW6Zh3t0bElXV7LAUmUI2obgPod93EBUawSrFJ09IHNTNO0S+06gKASaVuBwEDgMAn0vULcSIq8h8i1E2UD6Eaw0z+FHAaI0QbXdwrv1IKWEIgUmBrOEUvJibVuC1QhlSxArSGVDMkMSwVoulyAhoO3NzQ2EEMYhpDQgSZDy5ayhzyzJ+EhKsImTRohFRAiY4THD0SAycImgiBAzY00Emwir6c6d7qMpXttbopFQBwRESJmNDc2ZERMjJUZIjHxKiIgN6XrK0WfH5Iz3Wr21nirrKjpAE+vqmvBMnF2KMZZSwpviQ2b4kwBvrlC3zRPsqZ1ze/bM8pRE03mOs89aCjE+xGXgL/Y90JT4kc/a1Q3qeockSRDFCeI4QeD7pghN6uc4J37ks+6OJ+z3ezRti8Nwh5XnfZn4GSzHcbD2AwTBCNYLOmvtvwmLskK1qQzRYrH4lOyaAlYUx0iSFHGSIAxDKKXeJM6H/tVcLd/3DVmaZUjT9BXhmUir/+p1L6+cZTnadg/btl+1bL5TInieh812i6Io4Tg2lLJN7BnzmV9mWFalUVeWJYqiQJZlyIsCQeDDcVwU1cas1UioXuFNy08/fuL0eELTNri/P+IwDOj6zhCvVis4rot+uEOaJNe1/HB6xMPpYdzDw4Cu6wz0wkdRBNd1sdvVpvVrlt3Sy/z0/GyWuut7/PrzG1VV4ttiYf6RWuHw/WjG8e+83l2bum0Qx7EJ1kuuFc1f1ShsWvM5XrOHfwGmKDQB9Md9SgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/60eefaea18cf7b3982e52de61b59a651/f2205/04.png&quot;
        srcset=&quot;/static/60eefaea18cf7b3982e52de61b59a651/8ff5a/04.png 240w,
/static/60eefaea18cf7b3982e52de61b59a651/f2205/04.png 476w&quot;
        sizes=&quot;(max-width: 476px) 100vw, 476px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;run-npm-init-to-create-a-simple-packagejson-file-for-the-build-command&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#run-npm-init-to-create-a-simple-packagejson-file-for-the-build-command&quot; aria-label=&quot;run npm init to create a simple packagejson file for the build command permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Run npm init to create a simple package.json file for the build command&lt;/h3&gt;
&lt;p&gt;Open CMD, change directory to the folder that you cloned from Github and run&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 545px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9eae3aa7fadf8b45340b13a6ecf98884/3ddad/05.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABfElEQVQoz2WR25KbQAxE+YsYMIbBrjUw9yvsLuQ1yf9/UKek2M5W8tCFRqU6ajXVkCXG3aF5l6hnAdN7pG6Fli3K+g1S1SilxradsG01QjjBuhN8qPmdcoOz9GhtRidGVJ/7juM4kEKE0QbWexhnYJ1DzCuc87DOI8YEpS1yLggxQ2sL5wPe3u4Qw4BRCFwuF1TaGEil4EOA0gohJFhrkfKKlDNCjDDGIMbAfeccyrpxL6UM6wjs2QCBK601w8xjeF4WUE8pxZrmGcZY0GKlNKZpgpQS92nmuev1irpu0LYtuq5DRRDaQPLB8xBtF0Kg73v0w8D1OI6v3jAMLIKJx6lPsUNtLPzDdimFHYQQkMv66seUeBHVFAPFI8SA8/nMIHL3ABr8gVJOEWVdoZTkc70PnGPOmfOjnzNNd4zjlR33/V/QyyHlsywLA6imLClTEp1EDpqGMmr4S++nnrCv0OrHz194//jA535gP74z+JkpOb7dbv+5+Kp/Hf4GolALWiBg38wAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/9eae3aa7fadf8b45340b13a6ecf98884/3ddad/05.png&quot;
        srcset=&quot;/static/9eae3aa7fadf8b45340b13a6ecf98884/8ff5a/05.png 240w,
/static/9eae3aa7fadf8b45340b13a6ecf98884/e85cb/05.png 480w,
/static/9eae3aa7fadf8b45340b13a6ecf98884/3ddad/05.png 545w&quot;
        sizes=&quot;(max-width: 545px) 100vw, 545px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It will give you the basic &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file. Initially, I want to keep both frontend and backend in the same folder. So this package.json is merely to contain the build command for each frontend and backend. Nothing fancy about that. It is a place to have the information about the project and be as detailed as possible, such as name and description.&lt;/p&gt;
&lt;h3 id=&quot;create-a-new-angular-application&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#create-a-new-angular-application&quot; aria-label=&quot;create a new angular application permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Create a new Angular application&lt;/h3&gt;
&lt;p&gt;After that, run &lt;code class=&quot;language-text&quot;&gt;ng new frontend --skipTests=true&lt;/code&gt; to create a new Angular application with the default name &lt;code class=&quot;language-text&quot;&gt;frontend&lt;/code&gt;. The flag &lt;code class=&quot;language-text&quot;&gt;--skipTests=true&lt;/code&gt; tell CLI not to generate “spec.ts” test files for the new project. I am skipping the test for now.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 749px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b4fae177cc3439e763c06001b6fda6eb/5bb8b/06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.249999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAp0lEQVQI113OSw6CMBhFYXZhovw1BimP4iOCVhGhRTG6/wUdY9CBDk7u5A6+oKyPlG3NrrFUW4tvei5tSVUVrLYxqZmTJIJOhDwLWcQRYaRRSqFEEKV+CvZdS++uPIYnzntu9wfe3/HdQN14UrNBlimzRcxUF4TLFNEZkq2QfD1uYpD8/dMEE5uxsRXOdbi+53g6Ye2Bc3OmMAYJZ6NGyadRov5kX+ELRMRaCqB82+gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/b4fae177cc3439e763c06001b6fda6eb/5bb8b/06.png&quot;
        srcset=&quot;/static/b4fae177cc3439e763c06001b6fda6eb/8ff5a/06.png 240w,
/static/b4fae177cc3439e763c06001b6fda6eb/e85cb/06.png 480w,
/static/b4fae177cc3439e763c06001b6fda6eb/5bb8b/06.png 749w&quot;
        sizes=&quot;(max-width: 749px) 100vw, 749px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I select to use Routing and SCSS for the styling. That’s how it should look after running the command. Now you can start writing code already.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 314px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2c836ee3f56e1d864d4101cd3712f794/5b158/07.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 140%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADjklEQVRIx42V2W8bVRyF/V5aiSUSFNQ4nrnr7DN37LHHjhMnDSFAH9oCUYugCNGoAvHCC0Lib//QvVZC0iauH45GmuXT+S33zCAWmrNVx6sXZ8RdgZ2V2AOH7SqqozHNWU86LohHEUIIRPyu4ihGJQZlNYMHRxd8+vWffPXrG9IXf5G9/Af3y7/I87+5d3zBg5M33D+64IPV61t1f3XBvcPf2Dn9g53T3xns9N/xxeFLFk9+wJ29onj6GvfsNfL0Zz6cPOWT6XM+3qTZcz7qnvHZ8pyHy3MGajTE6phK5OznDj21RGLI8POHyL1dxPBRuPr31Gj3Fg2Rw12MjoIGQmq0SpkdOL598ZgsqWjHY+qmpW4c48kUm6REsSQW6k5FkQwaCClJTEre5lRHFWlVkqQpxlisTUiSFKUUsR+AkLfKP5NGBw38jdSkiFLzqBekbYkwa0AsBFEUX8HCvVvAcSRQ1gQNlNLs7Y2Yz5ccr75EtxnV4ynZrKY67ij32/DBXe4ugdJqdGYZKKkYCcm8SPmma9DTKkDyvsZW2Y2yNgG9uyvgnlQspeH7okHNmwAsFo7EFTdgd0HXDq8Bh0pyLHJ+NBNEX1EuHPncUSwabJ0Rj+ItSr4EhgkammqPk1WE7RzF0pEvHOXhBJ0l4Wi9z+FVyVIpRCxJy4TuuCMZl8FdOllffR+3GorR6NQDpUIKiUxTkn5KNq3IF+0auO/Iuip8sKmPV1O+BAqlyaKY1u9k31DsN5QHE5K2pFi2VEcdSZtflb6xh5fAVEicMaQzD3SUyzHZrAlOfTn+RG015VCy0lghKawh9yuz74Ir7y5pi61KVjeAPiCkILGKvHcU/XrKvn9JkxPtxVv08BpQScnIFKimJ5vkAeblHfrB+CX3iXzXtN9x6IGRKTCuJ+vW6+JhXllfo/MEsSFx7gRq5/8feQiG4G7eoIuEaBhdudu65NjkWDcLDv1QiuU4nOlyNaE+mWKbzWtzzaEOwKEtiCsfWwXZwpHOahKX409SWJv3hoO+todC0BUVaeGo+5Z6MaGejzG5JRrFN4L11pJ9Ymu9Dlh/YxQLfprU9K7h5OSMw8Uhi1mP1mZjDr4dbd7p4PJBV4+oy4rVcsXBckXbuJDkPjg2lvvW7yEAYyGZWkFV5LTTCe1sQtXW2DxBabWVS99rpfX/wCqS5FVOMa2pFy313FHPHMrorYDKGExi+Q+iz0yZDzgfpQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/2c836ee3f56e1d864d4101cd3712f794/5b158/07.png&quot;
        srcset=&quot;/static/2c836ee3f56e1d864d4101cd3712f794/8ff5a/07.png 240w,
/static/2c836ee3f56e1d864d4101cd3712f794/5b158/07.png 314w&quot;
        sizes=&quot;(max-width: 314px) 100vw, 314px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Go ahead and commit all of the changes.&lt;/p&gt;
&lt;h2 id=&quot;3-configure-tailwindcss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-configure-tailwindcss&quot; aria-label=&quot;3 configure tailwindcss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Configure TailwindCSS&lt;/h2&gt;
&lt;p&gt;Before going deeper, we need to create a new branch for configuring TailwindCSS. Open cmd and run.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt; tailwind-configuration&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;/blog/configure-tailwind-css-with-angular/&quot;&gt;detailed article&lt;/a&gt; that I wrote a few months ago for Tailwind configuration manually has several issue recently. I recommended using &lt;a href=&quot;https://github.com/ngneat/tailwind&quot;&gt;ngneat/tailwind&lt;/a&gt; to add TailwindCSS into your Angular application.&lt;/p&gt;
&lt;p&gt;Run the below command and you are all set.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;ng add &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ngneat&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;tailwind&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;test-if-it-is-working&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test-if-it-is-working&quot; aria-label=&quot;test if it is working permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Test if it is working&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f09e9b5bc6c340aa82d6dc765042ace3/29114/09.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB2ElEQVQoz42RTW8SURSG508Y2/keZgbmG2gXaqP9UkDBGJNu/AfG1IU7l8aFJuq+G/8ACcRS2n+kriGAoswMj5kBibXV9CRv3nNPcp57zr1CEEY4fshGFFL1XQLXwSkVMQsFDF1HkSU8P6DdbtPvn9Dtdul0OnQ7XY6PP3HWP6XXP+O01+ek10Pwoiq2FxEGAcWCjmksZOgaBcNAlkQqlQqzOObPiFNImfN3CH4YoukGWjaNqqKqau6KsshFUSSKIgaDQd6QxHEO/zad8WP2kziOSZKUJEnyXHAcJ29SFOWcVFVZAT3PYzgc5sB0fnGq+bKWpimC73tYloVl2ZimiVkwMQwDXddzz+C+H6yAWfNlWgGz2x2nSKlk55BM6nJ1TdOQJAnXdc8B/zthtrIsy4u3W617GXBwRaDrIssZRLsoTUfMgd7qU7Kmf629ANomsngdTV5HzSQtXBHX8pq0dg2vZDMajbhKCM6tGqJdRSptIhU3EZeuhTdRvBusWxvY5dt8+fyV6fR7Dp6Mx0wm49zHS/0+C+Xtu9TePKP+7jn33h6y8/6Q+ocXlPce8vjoNQcfX/Ho6CUHT5+wu7XF7s4d6vvbNGv73K/t0Wg2aLWaPGjUaDUb/ALacjWDqXf4VgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/f09e9b5bc6c340aa82d6dc765042ace3/d9199/09.png&quot;
        srcset=&quot;/static/f09e9b5bc6c340aa82d6dc765042ace3/8ff5a/09.png 240w,
/static/f09e9b5bc6c340aa82d6dc765042ace3/e85cb/09.png 480w,
/static/f09e9b5bc6c340aa82d6dc765042ace3/d9199/09.png 960w,
/static/f09e9b5bc6c340aa82d6dc765042ace3/07a9c/09.png 1440w,
/static/f09e9b5bc6c340aa82d6dc765042ace3/29114/09.png 1920w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The powerful &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; syntax also works perfectly.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bd8d2ded25ec4e21c60cf9c4e77754f0/29114/10.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACUUlEQVQoz42SzW4URxSF+yUQZvq3quuvq6d7xtPGxh4ZGyVYQtmzZU0SKdnkBSIlCgmRnMGS/QggMWAY81pmhUimx3zRFERBWeVKn25t6tQ53SdqmpbKGZw1SCnJ85w8y0jTNBDHMcZonj17ymKx4MV8zvz5c+bzOefnL3mzuOB8ccGb1xe8fvWKqG7H6FKgjSYvCgohyYUICFkSJynDuqLvl3w+yyu4+vCB/07UjFqsUmjrsa7COI9UmuKTYJplVL7i8u3bcGHV9yz7nnfvl/y5/Iu+71mtrrharcI58r4iy1KyddS8CDGTJEUUBVIIkiTBe8/l5WUQ/CiwYrX6Z//LOkVU1Z5cSspCYHSBlBmiSCiVRlkXHqjrOgj9n4naUUvVOJS3GK/wQ0011CinyaRkMBiglOLx4985Pj5mNpsxe/LkI7MZJycnnJ6eBs7Ozoi6ySbTw03U7Q6/d5P9/R26wzFyaENsIURwWWhLLlVoQZEm4VOsGyCkRJZlaEhZlkT1sMFVHmsdpVIYa1FaI4QMf3xNlmW0ky1u7d9hd3eP6fYW29s7jCYdkzuH7Eyn1L6m8p7IKclg4xrp4Hog+bTTG9fJ4o1AGm/gmhHjbovJeIzxdXC+dlXtT+n29jDrpmhN5HbvMtCbxHaL2HTEtgs7b25xw3QMdEfmbqKNC861NjjncNaitaKZTBi2LVVVUa8djm5/wd2fHnL06Fu+/PlrDn79hqPfvqc9+IqDH7/j3h8/8ODpLxzcP6IYJCglqK2irQzOCKpRTdvUeKsZesffzePGLL6npcwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/bd8d2ded25ec4e21c60cf9c4e77754f0/d9199/10.png&quot;
        srcset=&quot;/static/bd8d2ded25ec4e21c60cf9c4e77754f0/8ff5a/10.png 240w,
/static/bd8d2ded25ec4e21c60cf9c4e77754f0/e85cb/10.png 480w,
/static/bd8d2ded25ec4e21c60cf9c4e77754f0/d9199/10.png 960w,
/static/bd8d2ded25ec4e21c60cf9c4e77754f0/07a9c/10.png 1440w,
/static/bd8d2ded25ec4e21c60cf9c4e77754f0/29114/10.png 1920w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Looks good. Now I will commit it and then push it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 720px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b73a54d789b1d5c61c8a3c2443c5aaf4/37523/11.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACGUlEQVQoz12TW3LjIBBFtY1YIEdvQBKSQLIedpyZ/S/qTIFTmcx8nIKmqrtvXyAZPzbaY6bQDWM1Y7Vm9iXWlixrybKUjGOBtQXeF8yuws05tXpHNB2yUci2e1HWJLdlwU0zy+Lxq8MvC7Nbcc6z3g6cvzFN4fzGODlaZaiqBpFKMpGSCfFFSiYFidEG3SqauqYbOkzXoU2P0oauG1Cqox9GtDakqeTylnK5CKSQP4p9ISWJrHOEKpDFO02mqPMcpSVte0WbLNK2L5SS32tZXxFVi6xqZNUgwz4vSdznQX9fmLaFY7pz33eOs8d7w3EY7g/LeR8ZxwpjBFrLuNbtO6LWyLpFNvpFUZF8Pp/8/vzNeZzsZyh2Ms0L1k5kWc7bm3iNehFcLhIhMtI0++HhD4KHj48Pnp+/2Lad8/4gxIv3fDyfjKOlLEuapqGuK/I8R4TELPtG/keyHwePx4P1diPsA9u+x7gfBqy1dH1P13UY08UGwfw0TRFCIKWMxOLhUm7bTt/3MXGwA3YcI8YYZue/42EYGAYbn1Xbtmitqev6r9KvosmyrhRFwTRNzG7GLx5rR5z3rOvKGM89zrnYcHYusq435tlFIUqpb6XJeT/jiKFYSL4/Huz7EUfeti0+8Nu2YYyOqpTWX16Kf7y8Xq8vhdYOUU3oFEYLhcJoQUWIg49hzKDQ+ddPetljGceJVqn46IPP4fL+AAg5ZN6pWkiJAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/b73a54d789b1d5c61c8a3c2443c5aaf4/37523/11.png&quot;
        srcset=&quot;/static/b73a54d789b1d5c61c8a3c2443c5aaf4/8ff5a/11.png 240w,
/static/b73a54d789b1d5c61c8a3c2443c5aaf4/e85cb/11.png 480w,
/static/b73a54d789b1d5c61c8a3c2443c5aaf4/37523/11.png 720w&quot;
        sizes=&quot;(max-width: 720px) 100vw, 720px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-configure-prettier&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-configure-prettier&quot; aria-label=&quot;4 configure prettier permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Configure Prettier&lt;/h2&gt;
&lt;p&gt;To make the code looks consistent in all the coding environment. I install &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&quot;&gt;Prettier for VSCode&lt;/a&gt; and create a &lt;code class=&quot;language-text&quot;&gt;.prettierrc&lt;/code&gt; file for storing configuration.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 365px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/12cc0e42162f0643ce535fe65a0ba222/2e8d1/12.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.74999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACAElEQVQ4y3WS2XKjMBBF/RsTEAghYRA7eMF24hinyllq3ub/v+VMIeIklTgPt2ih7qPeFjbPuX84cj6PdG1HXuQYo9HaoHWCMbPms/ly/rz7qkVVWuqmcbJZhtYapdS7g/kEacNyuXT313+zPfnHxPGsxb+XDUVR0vUdfdfRtC37/Z62bbE2Y7PZMgw7yjyntClTRbthYLvdUNW1AwZBgBDCaSFlRBhGNLUhtxGeHzgH3/cQvj9/RYC4Bvn+R/BVk/9ViyiKXIk6VoRhgIwT6t2J9uFC93ChPT5TrAYiFSPtiqToCYP5geCGFlJKB4wihe95aFtS7U904yvd+MLq6S/LvKaoLYfzASnVXOJvwCnDqZmRUgjhE6cF/eMzzfFCP76Q9wNplpAkMUpJV0UQiI++3QS6DN+BKklpDk/0p2eawxkVa+KswVRbpEq48zzufIHvYOKjt59Apb4BLdXuRLk90h7OFOs99fEVu77HxJo21fSZRqvodobTlNU7dJqgzmuq4Ui9H1mNr6yf3ly2k6a9zItpZ9fOdsBvE597qBQT2PM84qygH9/oHi+kVU++HUnbgbRqkZFCRAa5LJ3/zQzdQCJFVWXY3CACiW3XmNQiQ4k0FqmX+N4fFxBOAxH+bIfhD7m1mRa3rJYUhcHzfIR358p3r07BQhCG8kfwLeh/UO13UhPFLdgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/12cc0e42162f0643ce535fe65a0ba222/2e8d1/12.png&quot;
        srcset=&quot;/static/12cc0e42162f0643ce535fe65a0ba222/8ff5a/12.png 240w,
/static/12cc0e42162f0643ce535fe65a0ba222/2e8d1/12.png 365w&quot;
        sizes=&quot;(max-width: 365px) 100vw, 365px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;5-configure-some-basic-tasks-runner-for-the-project&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-configure-some-basic-tasks-runner-for-the-project&quot; aria-label=&quot;5 configure some basic tasks runner for the project permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Configure some basic tasks runner for the project&lt;/h2&gt;
&lt;p&gt;Open the &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; on the root folder (not the &lt;code class=&quot;language-text&quot;&gt;frontend&lt;/code&gt; folder) and add the following content. You can do this step later. You can skip it for now. Only when we are ready to deploy the code somewhere else, then we need it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 638px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4a2df69395679e0d8eb25153ad20a5bf/41be6/13.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABW0lEQVQoz5WSyXLbMBBE9R8hCBDrYOEWW5ZdkXWI/f+/9FKkZKWiXJJDF3jAPPZ041BrY9M0z5QYGXNhWVfGccRaS9/3/6WDtYZaBZGMSEJEkJSopVCyMBiDUuqfYNu9g+4VoYzI8oykyFyERQLZO1p0DEbTdR2dUn/oW/fw3akrsFeK2FaW80/G05mcM2OwzMlTgyPHSE6J7O3+E/GW6AbG5MnBkZylRb/LGM1BW4dLZQdOL2ekraRxobSZ4D01bgr78DY0pevwtsUGM1pjjcEafc2wl4Lue8rTK/XpjXZ6J7eJqVWWkjhW4VgT30tkzZGWPMEOuMHc8u3vq18zTAUzWJbzB+v7J9PrhRIcp5a4LJXL2vbzpV3Bz1V2V1+Z9Q+FHbRUtNbU4w9kXPbsZgm7m83VpuTtvWl1a/Ox3d9A5wiDJ9aJwXmSNffwv7Q5UreV7mC1Af5+Tr8ArcUAuGIYYSoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 01&quot;
        title=&quot;Angular Jira Clone Tutorial Part 01&quot;
        src=&quot;/static/4a2df69395679e0d8eb25153ad20a5bf/41be6/13.png&quot;
        srcset=&quot;/static/4a2df69395679e0d8eb25153ad20a5bf/8ff5a/13.png 240w,
/static/4a2df69395679e0d8eb25153ad20a5bf/e85cb/13.png 480w,
/static/4a2df69395679e0d8eb25153ad20a5bf/41be6/13.png 638w&quot;
        sizes=&quot;(max-width: 638px) 100vw, 638px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Phew, that’s all for part one. You have done some basic configuration to get ready for the exciting part: coding.
I was trying to cover as detailed as possible but I might get lost on the way. Thanks for understanding. All your comments and feedback are welcomed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;source-code-after-finishing-tutorial-one&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-after-finishing-tutorial-one&quot; aria-label=&quot;source code after finishing tutorial one permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code after finishing tutorial one&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/tree/tailwind-configuration&quot;&gt;https://github.com/trungvose/jira-clone-angular/tree/tailwind-configuration&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular Jira Clone Part 00 - Prerequisites]]></title><description><![CDATA[You should know some basic Angular, TypeScript and git knowledge before starting]]></description><link>https://trungvose.comangular-jira-clone-tutorial-00-prerequisites/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone-tutorial-00-prerequisites/</guid><pubDate>Sun, 28 Jun 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Before following through the tutorial, you should know some basic stuff.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Angular&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic concept such as &lt;a href=&quot;https://angular.io/guide/template-syntax&quot;&gt;interpolation&lt;/a&gt;, component, &lt;a href=&quot;https://angular.io/guide/attribute-directives&quot;&gt;directive&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://angular.io/guide/reactive-forms&quot;&gt;Reactive Forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://angular.io/guide/router&quot;&gt;Router&lt;/a&gt;, lazy loading module&lt;/li&gt;
&lt;li&gt;How to &lt;a href=&quot;https://angular.io/guide/component-interaction&quot;&gt;communicate between parent and children component&lt;/a&gt;, @Input and @Output&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Typescript basic: &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/interfaces.html&quot;&gt;interface&lt;/a&gt;, &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/classes.html&quot;&gt;class&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Package Manager: &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RxJS operators: &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt;, &lt;a href=&quot;https://www.learnrxjs.io/learn-rxjs/operators/combination/combinelatest&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;combineLatest&lt;/code&gt;&lt;/a&gt; and so on&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.atlassian.com/git/tutorials/comparing-workflows&quot;&gt;Git&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a commit&lt;/li&gt;
&lt;li&gt;Push the changes to remote&lt;/li&gt;
&lt;li&gt;Git merge and pull request&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How to use command line on Windows (Because I use Windows environment, it could be different on MAC)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Why involves using a command line? Which in the past was never required as a frontend dev. It is just because of the modern front end tooling utilize the CMD a lot more than before. If you’ve never used one, you can read &lt;a href=&quot;https://www.learnenough.com/command-line-tutorial/basics&quot;&gt;this tutorial&lt;/a&gt; to get a good overview to get started. For better or for worse, knowing how to use the command line is an important part of modern JavaScript (and it opens up doors in other areas of development as well).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In short, that is how it looks on Windows. Press &lt;code class=&quot;language-text&quot;&gt;Windows + R&lt;/code&gt; and type &lt;code class=&quot;language-text&quot;&gt;cmd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 399px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2a83c8e0a851fdd4dacb18aa9527485c/a307d/run.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABuElEQVQoz5WQWW+bQBRG+eX9Q33tUypVqipH6UPVqHYSGMISmx3jGZYANsupGNuNX3ulo2/RhRkw7CDD9ELSOKY/Hum6TtP3veaaP/jo9c4lN01DlmUYqqxYZh5Hbqeqat7blv+ZeZ4wDlLhyYHmNCEPBVGckGc5SZwQRxGH4kASx7pL40TfotgXpElCmuYU+z3BLmCf75lnMKJccicq8veRPPBxzBfePBchBLZlYT6/ICwTYVoI08SxbV6FINwFtG1L37U0dU13+RpDKXW9MIE68RS39MNId1qY6I4D/TDp3F78VYcZThNajxOcxhkjCkOE/QrDkc+bmk93GbKbOLQTRTve6NnfavE+aGQ7EsqOojlhSCkvP3RmmmatwzgxTrNmuOhy+pnpxl/zuVv2jDAMqaoKJRWlUpSlopCKdC/JijOLXw6W8qB3lJSaa16eWfzSGVEUUdc1ZVmiypK2qfnt5Xzf7Pjy45FvvwRf/4S425CX5yfuV/es1xvCKMJxXrlfrXh4+IllWaRpirGY5aVBEGjCMEB4W9bCZy08NsJjLd7wfB/PdXEcB9d18X1fqy3sf912u+UvwTz1McYZwDwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 00&quot;
        title=&quot;Angular Jira Clone Tutorial Part 00&quot;
        src=&quot;/static/2a83c8e0a851fdd4dacb18aa9527485c/a307d/run.png&quot;
        srcset=&quot;/static/2a83c8e0a851fdd4dacb18aa9527485c/8ff5a/run.png 240w,
/static/2a83c8e0a851fdd4dacb18aa9527485c/a307d/run.png 399w&quot;
        sizes=&quot;(max-width: 399px) 100vw, 399px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And then press &lt;code class=&quot;language-text&quot;&gt;Enter&lt;/code&gt;. That’s the CMD.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 500px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d77691f0ebe22e9acb526e02a3e1486e/0b533/cmd.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAo0lEQVQoz7WSSwrDMAxEvXTsEP8lG5p00/tfcYoSMC2kaQnp4iFhNMMYSc3zjGVZILXWCmZGrYzWGnLO8N7DOQdr7SFa61WrZHiapi4yxnReBeM4HiLzpRDUr6KjdG+Ge8JvaT4nLJvhWZPdL19nOIBkKdcmLH80PIMxtp+XHgYwEZQcrnduQ/pOQAhCRIwJKaW1CiHKW0QlQmVGowzOGY/7DU9roeaKoTjingAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular Jira Clone Tutorial Part 00&quot;
        title=&quot;Angular Jira Clone Tutorial Part 00&quot;
        src=&quot;/static/d77691f0ebe22e9acb526e02a3e1486e/0b533/cmd.png&quot;
        srcset=&quot;/static/d77691f0ebe22e9acb526e02a3e1486e/8ff5a/cmd.png 240w,
/static/d77691f0ebe22e9acb526e02a3e1486e/e85cb/cmd.png 480w,
/static/d77691f0ebe22e9acb526e02a3e1486e/0b533/cmd.png 500w&quot;
        sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I think that’s it. Are you ready for &lt;a href=&quot;/blog/angular-jira-clone-tutorial-01-planning-and-set-up/&quot;&gt;Part 01 - Create a new repository and set up a new Angular application with CLI&lt;/a&gt;?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[I built a Jira clone application with Angular 9, Akita and ng-zorro]]></title><description><![CDATA[I spent my spare time to build a cloned Jira app with Angular 9, Akita and ng-zorro in about two weeks time, including the weekend.]]></description><link>https://trungvose.comangular-jira-clone/</link><guid isPermaLink="false">https://trungvose.comangular-jira-clone/</guid><pubDate>Sat, 27 Jun 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There have been a handful of cool Jira-cloned apps written in &lt;code class=&quot;language-text&quot;&gt;React&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;VueJS&lt;/code&gt;, which makes me wonder &lt;strong&gt;Why not Angular&lt;/strong&gt;? And here you go.&lt;/p&gt;
&lt;p&gt;This is not only a simplified Jira clone built with Angular 9, but also an example of a &lt;strong&gt;modern&lt;/strong&gt;, &lt;strong&gt;real-world&lt;/strong&gt; Angular codebase.&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/jira-clone-angular&quot;&gt;https://github.com/trungvose/jira-clone-angular&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;working-application&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-application&quot; aria-label=&quot;working application permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working application&lt;/h2&gt;
&lt;p&gt;Check out the &lt;strong&gt;live demo&lt;/strong&gt; -&gt; &lt;a href=&quot;https://jira.trungk18.com&quot;&gt;https://jira.trungk18.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/src/assets/img/jira-clone-angular-demo-trungk18.gif&quot; alt=&quot;Jira clone built with Angular 9 and Akita&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you like my work, feel free to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;text-left&quot; title=&quot;Thanks for your support!&quot; href=&quot;https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2Ftrungk18%2Fjira-clone-angular&amp;text=Awesome%20Jira%20clone%20app%20built%20with%20Angular%209%20and%20Akita&amp;hashtags=angular,akita,typescript&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;icon-twitter&quot;&gt;&lt;/span&gt;Tweet&lt;/a&gt; about Angular Jira clone&lt;/li&gt;
&lt;li&gt;⭐ this repository. And we will be happy together :)&lt;/li&gt;
&lt;li&gt;&lt;a class=&quot;text-left&quot; title=&quot;Thanks for your support!&quot; href=&quot;https://www.buymeacoffee.com/trungvose&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://res.cloudinary.com/dvujyxh7e/image/upload/c_thumb,w_140,g_face/v1596378474/default-orange_uthxgz.jpg&quot; alt=&quot;Buy Me A Coffee&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks a bunch for stopping by and supporting me!&lt;/p&gt;
&lt;h2 id=&quot;who-is-it-for-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#who-is-it-for-%EF%B8%8F&quot; aria-label=&quot;who is it for ️ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Who is it for 🤷‍♀️&lt;/h2&gt;
&lt;p&gt;I have been working with Angular for about four years. I built cool stuff at &lt;a href=&quot;https://www.zyllem.com/&quot;&gt;Zyllem&lt;/a&gt; but almost all of them are internal apps which is difficult to show.&lt;/p&gt;
&lt;p&gt;This is a showcase application I’ve built in my spare time to experiment the new library that I wanted to try before: &lt;code class=&quot;language-text&quot;&gt;Akita&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;TailwindCSS&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;ng-zorro&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are many Angular examples on the web but most of them are way too simple. I like to think that this codebase contains enough complexity to offer valuable insights to &lt;strong&gt;Angular developers of all skill levels&lt;/strong&gt; while still being &lt;em&gt;relatively&lt;/em&gt; easy to understand.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This piece of work is also part of our technical series &lt;a href=&quot;https://github.com/angular-vietnam/100-days-of-angular&quot;&gt;angular-vietnam/100-days-of-angular&lt;/a&gt; which aims at enabling everyone, after 100 days of learning Angular with us, to &lt;strong&gt;self-build their application with the similar scale&lt;/strong&gt;. Our desire is to advocate and grow the Angular developer community in Vietnam.&lt;/p&gt;
&lt;h2 id=&quot;tech-stack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tech-stack&quot; aria-label=&quot;tech stack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tech stack&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/src/assets/img/jira-clone-tech-stack.png&quot; alt=&quot;Tech logos&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cli.angular.io/&quot;&gt;Angular CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://datorama.github.io/akita/&quot;&gt;Akita&lt;/a&gt; state management&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nestjs.com/&quot;&gt;NestJS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;UI modules:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;TailwindCSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Angular CDK &lt;a href=&quot;https://material.angular.io/cdk/drag-drop/overview&quot;&gt;drag and drop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ng.ant.design/docs/introduce/en&quot;&gt;ng-zorro&lt;/a&gt; UI component: &lt;code class=&quot;language-text&quot;&gt;tooltip&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;modal&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;icon&lt;/code&gt; and more.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/KillerCodeMonkey/ngx-quill&quot;&gt;ngx-quill&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;features-and-roadmap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#features-and-roadmap&quot; aria-label=&quot;features and roadmap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Features and Roadmap&lt;/h2&gt;
&lt;p&gt;I set the tentative deadline for motivating myself to finish the work on time. Otherwise, It will take forever to complete :)&lt;/p&gt;
&lt;h3 id=&quot;phase-1---angular-application-and-simple-nest-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#phase-1---angular-application-and-simple-nest-api&quot; aria-label=&quot;phase 1   angular application and simple nest api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Phase 1 - Angular application and simple Nest API&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;June 13 - 27, 2020&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Proven, scalable, and easy to understand project structure&lt;/li&gt;
&lt;li&gt;Simple drag and drop kanban board&lt;/li&gt;
&lt;li&gt;Add/update issue&lt;/li&gt;
&lt;li&gt;Search/filtering issues&lt;/li&gt;
&lt;li&gt;Add comments&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Noted: All of your interactions with data such as leave a comment or change the issue detail will not be saved to the persistent database. Currently, the application will serve a fixed structure of data every time you open the app. It means if you reload the browser, all of your changes will be gone.&lt;/p&gt;
&lt;p&gt;Phase 2 will bring you a proper API where you can log in and save your work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While working with this application, I have the opportunity to revisit some of the interesting topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TailwindCSS configuration - that’s awesome&lt;/li&gt;
&lt;li&gt;Scrollable layout with Flexbox&lt;/li&gt;
&lt;li&gt;Deploy Angular application to Netlify&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will take two weeks break to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fix bugs and UI enhancements for Angular Jira clone.&lt;/li&gt;
&lt;li&gt;Continue working with the &lt;a href=&quot;https://github.com/trungvose/typescript-data-structures&quot;&gt;typescript-data-structures&lt;/a&gt; repo.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;phase-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#phase-2&quot; aria-label=&quot;phase 2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Phase 2&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;July 10 - 25, 2020&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Refactor the mono repo to use Nx Workspace&lt;/li&gt;
&lt;li&gt;GraphQL API and store data on the actual database&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Websocket realtime update&lt;/li&gt;
&lt;li&gt;Interactive report&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tutorial&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tutorial&quot; aria-label=&quot;tutorial permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tutorial&lt;/h2&gt;
&lt;p&gt;When I look at the application, it is huge. When the task is huge, you usually don’t know where and how to start working with them. I started to break the big task into a &lt;a href=&quot;https://trungvose.notion.site/Angular-Jira-clone-Phase-1-79d3205e26024357a75ebfc00aba558e?pvs=4&quot;&gt;simple to-do list on notion&lt;/a&gt;. Once I know what needs to be done, what I need is to follow the plan. That’s my approach.&lt;/p&gt;
&lt;p&gt;I learned a lot of stuff. I know you might also have a curiosity about the process of building the same scale app as well. That’s why I am writing a tutorial series on how I built Angular Jira clone from scratch. I hope you guys will learn something from that too :)&lt;/p&gt;
&lt;p&gt;I will try to be as detailed as possible. Hopefully through the tutorial, you will get the idea and will start working on your own application soon. Please bear with me.&lt;/p&gt;
&lt;p&gt;Its series will also be published in Vietnamese as part of our &lt;a href=&quot;https://github.com/angular-vietnam/100-days-of-angular&quot;&gt;angular-vietnam/100-days-of-angular&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;00&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://slides.com/trungvose/behind-the-900-star-repository-jira-clone-angular&quot;&gt;Behind the 900 stars repository - Slide&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;00&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://youtu.be/3dukbsRX0tc&quot;&gt;Behind a thousand stars repository - Angular Air&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;00&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-00-prerequisites/&quot;&gt;Prerequisites&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-01-planning-and-set-up/&quot;&gt;Create a new repository and set up a new Angular application with CLI&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-02-application-layout-tailwindcss-flex/&quot;&gt;Build the application layout with flex and TailwindCSS&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-03-akita-state-management/&quot;&gt;Setup Akita state management&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-04-editable-textbox/&quot;&gt;Build an editable textbox&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;05&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-05-interactive-drag-and-drop-board/&quot;&gt;Build an interactive drag and drop board&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;06&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-06-angular-markdown-text-editor/&quot;&gt;Build a markdown text editor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;07&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-07-rich-text-editor/&quot;&gt;Build a rich text HTML editor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;08&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/blog/angular-jira-clone-tutorial-08-angular-placeholder-loading/&quot;&gt;Create placeholder loading (like Facebook’s cards loading)&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Done&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;time-spending&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#time-spending&quot; aria-label=&quot;time spending permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Time spending&lt;/h2&gt;
&lt;p&gt;It is a side project that I only spent time outside of the office hours to work on. One day, my team and I were fire fighting on PROD until 11 PM. After taking a shower, I continue with Angular Jira clone for another two hours…&lt;/p&gt;
&lt;p&gt;According to waka time report, I have spent about 45 hours working on this project. Which is equivalent to watch the &lt;a href=&quot;https://www.bingeclock.com/s/stranger-things/&quot;&gt;whole Stranger Things series twice&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I really enjoyed working on this project. The interactive kanban board took me sometimes, it is challenging but exciting at the same time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/trungvose/jira-clone-angular/raw/master/src/assets/img/time-spending.png&quot; alt=&quot;Jira clone built with Angular 9 and Akita - Time spending&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;whats-currently-missing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#whats-currently-missing&quot; aria-label=&quot;whats currently missing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What’s currently missing?&lt;/h2&gt;
&lt;p&gt;There are missing features from the live demo which should exist in a real product. All of them will be finished on Phase 2:&lt;/p&gt;
&lt;h3 id=&quot;proper-backend-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#proper-backend-api&quot; aria-label=&quot;proper backend api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Proper backend API&lt;/h3&gt;
&lt;p&gt;I built a very simple NestJS API to send a fixed data structure to the client. All of your interactivity with data will only be saved on the memory. If you refresh the page, it will be gone. Phase 2 will bring the application to live by saving the data into a database.&lt;/p&gt;
&lt;h3 id=&quot;proper-authentication-system-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#proper-authentication-system-&quot; aria-label=&quot;proper authentication system  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Proper authentication system 🔐&lt;/h3&gt;
&lt;p&gt;I am currently sending the same email and a random password to the server without any check to get the current user back. Phase 2 will also bring a proper authentication system.&lt;/p&gt;
&lt;h3 id=&quot;accessibility-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#accessibility-&quot; aria-label=&quot;accessibility  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Accessibility ♿&lt;/h3&gt;
&lt;p&gt;Not all components have properly defined &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA&quot;&gt;aria attributes&lt;/a&gt;, visual focus indicators, etc.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-development-environment-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-up-development-environment-&quot; aria-label=&quot;setting up development environment  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting up development environment 🛠&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;git clone https://github.com/trungvose/jira-clone-angular.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cd jira-clone-angular&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;npm run start:back&lt;/code&gt; for the API&lt;/li&gt;
&lt;li&gt;The API server should run on &lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;npm run start:front&lt;/code&gt; for angular web application&lt;/li&gt;
&lt;li&gt;The app should run on &lt;code class=&quot;language-text&quot;&gt;http://localhost:4200/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;unitintegration-tests-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#unitintegration-tests-&quot; aria-label=&quot;unitintegration tests  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Unit/Integration tests 🧪&lt;/h3&gt;
&lt;p&gt;I skipped writing test for this project. I might do it for the proper backend GraphQL API.&lt;/p&gt;
&lt;h2 id=&quot;compatibility&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compatibility&quot; aria-label=&quot;compatibility permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compatibility&lt;/h2&gt;
&lt;p&gt;It was being tested on IE 11, Chrome and Firefox. For Safari, there are some minor alignment issues.&lt;/p&gt;
&lt;h2 id=&quot;author-trung-vo-️&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#author-trung-vo-%EF%B8%8F&quot; aria-label=&quot;author trung vo ️ permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Author: Trung Vo ✍️&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A young and passionate front-end engineer. Working with Angular and TypeScript. Like photography, running, cooking, and reading books.&lt;/li&gt;
&lt;li&gt;Personal blog: &lt;a href=&quot;https://trungk18.com/&quot;&gt;https://trungk18.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Say hello: trungk18 [et] gmail [dot] com&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;contributing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contributing&quot; aria-label=&quot;contributing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contributing&lt;/h2&gt;
&lt;p&gt;If you have any ideas, just &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/issues/new&quot;&gt;open an issue&lt;/a&gt; and tell me what you think.&lt;/p&gt;
&lt;p&gt;If you’d like to contribute, please fork the repository and make changes as you’d like. &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/compare&quot;&gt;Pull requests&lt;/a&gt; are warmly welcome.&lt;/p&gt;
&lt;h2 id=&quot;credits&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#credits&quot; aria-label=&quot;credits permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Credits&lt;/h2&gt;
&lt;p&gt;Inspired by &lt;a href=&quot;https://github.com/oldboyxx/jira_clone&quot;&gt;oldboyxx/jira_clone&lt;/a&gt; and &lt;a href=&quot;https://github.com/Datlyfe/jira_clone&quot;&gt;Datlyfe/jira_clone&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I reused part of the HTML and CSS code from these projects.&lt;/p&gt;
&lt;h2 id=&quot;license&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#license&quot; aria-label=&quot;license permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;License&lt;/h2&gt;
&lt;p&gt;Feel free to use my code on your project. It would be great if you put a reference to this repository.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://opensource.org/licenses/MIT&quot;&gt;MIT&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A painful Gatsby v1 to v2 migration]]></title><description><![CDATA[I have just migrated Gatsby v1 to v2 on my client application, and it is a painful experience]]></description><link>https://trungvose.compainful-gatsby-migration-v2/</link><guid isPermaLink="false">https://trungvose.compainful-gatsby-migration-v2/</guid><pubDate>Fri, 19 Jun 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This morning, I spent about four hours to migrate &lt;a href=&quot;https://www.nugit.co/&quot;&gt;my client website&lt;/a&gt; to Gatsby v2. Why this time? Because recently, we need to make a query for a special type of data that our source API only supports v2. It won’t work with Gatsby v1 anymore.&lt;/p&gt;
&lt;p&gt;After following &lt;a href=&quot;https://www.gatsbyjs.org/docs/migrating-from-v1-to-v2/&quot;&gt;gatsby migration guide&lt;/a&gt; to&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update all the dependencies to support v2&lt;/li&gt;
&lt;li&gt;Install React&lt;/li&gt;
&lt;li&gt;Manually import Layout to wrap in each page and template&lt;/li&gt;
&lt;li&gt;Update Layout to support Static Query&lt;/li&gt;
&lt;li&gt;Convert to pure ES6 import&lt;/li&gt;
&lt;li&gt;Import Link from Gatsby&lt;/li&gt;
&lt;li&gt;Import graphql from Gatsby&lt;/li&gt;
&lt;li&gt;Also, update the &lt;code class=&quot;language-text&quot;&gt;html.js&lt;/code&gt; to match with v2 content. I have to use it on my project to make use of jQuery and Webflow.&lt;/li&gt;
&lt;li&gt;After these above changes, I also remove the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; and run &lt;code class=&quot;language-text&quot;&gt;npm i&lt;/code&gt; one more time to make sure there are no caches or differences on the dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Life is not that easy, after finishing all of the above steps. I run &lt;code class=&quot;language-text&quot;&gt;npm run develop&lt;/code&gt; and received tones of errors. I have to manually fix it, mostly came from the query of my API, which is hosted on &lt;a href=&quot;https://www.datocms.com/&quot;&gt;DatoCMS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And after all of my efforts, I received the green line that I was expected.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 520px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/244b71122d663a4540adc957d7eb8512/69902/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA8UlEQVQY00WQ64rDIBCF+xhL7mlS0YK6mIsamyam3X3/NzqLw5L+GA4M+M3xu3TKYvYW1qVxsM4jhADBOaZ5hvceRVGgaRp0XYe+71FVFfI8R1mWZ6ZdysuXF2CTxrqu2LYnnHc43i8472GGEfE4MIwDrLV0aN02hMcD7MbogNIabdue0Es2CTAjsSwL9hjhnKMHUirMzmO2loDGGCwhUDLGwDmntkII1HX9AaaGt1Ehxhfevz8EHKcJz3jAmAFcCIK07ZXaSCkJpJQi2F2IE0bA67eHC57a7HGnFlprAienCab/v5VlGTlLTj9ZnP6Swz8Q75go+YPCvQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A painful Gatsby v1 to v2 migration&quot;
        title=&quot;A painful Gatsby v1 to v2 migration&quot;
        src=&quot;/static/244b71122d663a4540adc957d7eb8512/69902/01.png&quot;
        srcset=&quot;/static/244b71122d663a4540adc957d7eb8512/8ff5a/01.png 240w,
/static/244b71122d663a4540adc957d7eb8512/e85cb/01.png 480w,
/static/244b71122d663a4540adc957d7eb8512/69902/01.png 520w&quot;
        sizes=&quot;(max-width: 520px) 100vw, 520px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But when I pushed to Netlify, I saw a weird message. I didn’t google right the way and I thought it was because my &lt;code class=&quot;language-text&quot;&gt;gatsby-plugin-netlify&lt;/code&gt; was not up-to-date. And I tried upgrading it and deploy again, still the same error.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c416fe9fab5ef484c2461a15b22e480d/ec3e2/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABKUlEQVQoz21R2W6CQBTlubGLaGPFnREY1iGA6FAQMW3//49Oc6/gg/Hh5Ewmd+6cxfDKCiI7IdItvEOF4HSGLL8R6jM2SY5FkMLyFb68GHOZMM+8CDM3wtt6j9eVYB5gpM0V6ziHnZZYBIoH6dFE+HjfODBtiYmQMG2Pz+bOw7jH04Wq7mAFilVF1QWquYI+cXONuZ9gKny8WFuMljY/Hq0E84DHpQYtop/dguw2kMcagW7h5JrPqyiD5Ses8lHNMxhx1eLTCeEWmpWFuuX8KMutKtj6eOfyMKm8o1c7Wope+c2BkbU/mO4DXlJ0f8guv8yEpOlYHWX5sXU5N860z3LIdbinGcM/1tio4q6K2iaEfdtkW5Y1K9+lB26cmqfG5/LWPBVJTG7+AQWeyic3CWU3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A painful Gatsby v1 to v2 migration&quot;
        title=&quot;A painful Gatsby v1 to v2 migration&quot;
        src=&quot;/static/c416fe9fab5ef484c2461a15b22e480d/d9199/02.png&quot;
        srcset=&quot;/static/c416fe9fab5ef484c2461a15b22e480d/8ff5a/02.png 240w,
/static/c416fe9fab5ef484c2461a15b22e480d/e85cb/02.png 480w,
/static/c416fe9fab5ef484c2461a15b22e480d/d9199/02.png 960w,
/static/c416fe9fab5ef484c2461a15b22e480d/ec3e2/02.png 997w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 604px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d0723e75335b85692baa4813e68650ea/87254/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABlklEQVQ4y41TWW7kIBT0/Y82FxiNNJM4XrENXgCbvWv0aHfHP+nkSSUw8iuKKihCjNiPA1JKaK2h9Y6DvpWCUhree1DdbrdvEWJC4X3AxDl6xlBWFeq2BRvGPP/3XmKT6keEKSWQuCKmhO/qJ+qoiKuIMaLre5R1jUlwCDFjmCZMfMLbOuMXa9HpT5WvNr0rjDF7Rj7u+3F6aKD3HdT+xCuFwOeRnfdY1hV8nrEpiU0pLNuW14j06BnsJhFSgg/hS4QQQFyFsRZV2+Lto0LddWi6HlXT4KNpMHIOzUYc6wZ3NnwF4jHOocjsMT4lE/zZTOMtpXuKjzQvcwqBLMs4T1DEFNF2Hf6+l+gYAxuGPDZtCy4E+O8/kP0AFwOMMXDOwToHY2wW8agsiFImRZbkWpuDOc4mashr1uKwNvv5AP1HI136q/ocCu1G14RUiWXJIO8mIbBJCVVWMPOCeDZccQ2FLLIUChH2bEDddpm0H8a8AYVDxIIN4OOEVcpn4xUPQput8PdQ0uX5ZFzm8WJ+eIEYU74J/wF4jvQMMqd8fwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A painful Gatsby v1 to v2 migration&quot;
        title=&quot;A painful Gatsby v1 to v2 migration&quot;
        src=&quot;/static/d0723e75335b85692baa4813e68650ea/87254/03.png&quot;
        srcset=&quot;/static/d0723e75335b85692baa4813e68650ea/8ff5a/03.png 240w,
/static/d0723e75335b85692baa4813e68650ea/e85cb/03.png 480w,
/static/d0723e75335b85692baa4813e68650ea/87254/03.png 604w&quot;
        sizes=&quot;(max-width: 604px) 100vw, 604px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I google it and turned out it was because of Node version configuration on Netlify was not matched with the required version. See &lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/24389&quot;&gt;gatsby/issues/24389&lt;/a&gt; for more detail.
I ended up fixing it by setting a higher Node.js version in Netlify by setting an environment variable &lt;code class=&quot;language-text&quot;&gt;NODE_VERSION&lt;/code&gt; to value &lt;code class=&quot;language-text&quot;&gt;10&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 552px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f72349e5c89608b627efcaf8403fcad6/08c0b/04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABLElEQVQoz52TiW6DMAyGef/nW9dj6jKOqppWQm6SkPBPCSsDjUrVLH1gbOPYTlIMISCO4yYhxoe+RxQpYVXXIGWJqqlxJgSX6xWkJHg9HcGEQJJxHDdBYiFFqmIOWPx4T/KMLBcoQgjQxkAqBSkVhJTQ2oBxPutKG3AhIYSY4pTO/qR771eL5wqdc7AJa9Fbm7/73sLayZ799tefbPd3KmhZZZ4h5xycM3Rti7alCGF42J5WarPl1Qy1s5C2z4jeQHsH7T2UdxnzowtnoTIux/gQ/sy7iDGikQKNmiCC4Z1RfDCKM21xojdUvMMbvWH39YmKMxBGs532ZtXuXKGUEl3HwNjEGJ/b3a3jVKRHWVfYHY942e+xOxzgh2mGz6ddnEM3DPlGLEkb9V++ASR1XLzRADkPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A painful Gatsby v1 to v2 migration&quot;
        title=&quot;A painful Gatsby v1 to v2 migration&quot;
        src=&quot;/static/f72349e5c89608b627efcaf8403fcad6/08c0b/04.png&quot;
        srcset=&quot;/static/f72349e5c89608b627efcaf8403fcad6/8ff5a/04.png 240w,
/static/f72349e5c89608b627efcaf8403fcad6/e85cb/04.png 480w,
/static/f72349e5c89608b627efcaf8403fcad6/08c0b/04.png 552w&quot;
        sizes=&quot;(max-width: 552px) 100vw, 552px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 391px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/60b34d56b28f99d01f7efc57a7c10cf3/14e0c/05.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 91.25000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACeUlEQVQ4y5VTTW/TQBDNld/AD+AHIPErkBokWiqgIFUgCkd6QeKAAIkDXFAEFw5cECBx4EMcoIIDKlLVpqS0UEFTkjhxPuuv9a7d2I69fmjXcUhp08JIo/WuZp7fzLzJxHEM4cLEGUYcnEeIYy7vURSBcy5dfA/H7mWZ9D3iuwN4FGJ9/TuW80soFApYXMqj1wv3B5SJMZcX6m7jzbKC1Z/z2CZr6OciDHuSnfAUbC+XgKIUYQsbLWTvz+Hw7Dvknl1CoGbR2LwNr0sSEBxsAlQyNM02jl5/jiM35nHszmc8fnUVcWcSVvk42qVbCEPBLJAJgkB68v6Z9jnpIQC9/gT5hZOYzt3DoZm3ePBiFnF7HESZgvFrHEbnEyhrQNMMGIYBi9iwbQrdMECIDcoYKGUJoPi5ptxFr5kFb47j9dwVvP9wAUFjElblLLrqCaz+eInFEgUxt0BsCsO0JGC73YFpWZLpoGQxXK32EKScBVHOIWxOoFufAKmcAq2eBi1nodQ+omZ6YJTAtAiY48BxHDDGpPtBsLOHRC9AL47BUc/AqkzBrk6DqTNw6+fR2bwM36P/NJC+bERzgS31qQSlyhiYehGsPgO9cg3U2kAqrVFyGRZ7Jr0IUKJ/wZaSQ6d8E1rtERy70v+9ABst5l3CTgLjgd5k8qCUBGyUoP/emszwYyw3hiewEigF+5OMgwCHH/brUSrmUcNIYvqA+01t2IhlYGXlKzaKRaytfYPneXszHAUmVsoiBDaxwSiVIhYapLaNRrMJQghc1z2Y4WDynMsE4bqmoVpV0fV92ISgVCqj1WrtYPlfJfueJxkGQQ++78mddh13V8m/AUg0VYLjsFikAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A painful Gatsby v1 to v2 migration&quot;
        title=&quot;A painful Gatsby v1 to v2 migration&quot;
        src=&quot;/static/60b34d56b28f99d01f7efc57a7c10cf3/14e0c/05.png&quot;
        srcset=&quot;/static/60b34d56b28f99d01f7efc57a7c10cf3/8ff5a/05.png 240w,
/static/60b34d56b28f99d01f7efc57a7c10cf3/14e0c/05.png 391w&quot;
        sizes=&quot;(max-width: 391px) 100vw, 391px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That’s how I spent my weekend, almost five hours has gone…&lt;/p&gt;
&lt;p&gt;What about you?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to configure TailwindCSS with Angular and why you should use it]]></title><description><![CDATA[This post gives you a glance at TailwindCSS and how to use it with Angular. If you're reading this and you don't know what TailwindCSS is, where have you been?]]></description><link>https://trungvose.comconfigure-tailwind-css-with-angular/</link><guid isPermaLink="false">https://trungvose.comconfigure-tailwind-css-with-angular/</guid><pubDate>Sat, 13 Jun 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR: This configuration is tested on Angular 9. It is also working on the previous version of Angular such as 8.x.x. It is also working with AOT when you do &lt;code class=&quot;language-text&quot;&gt;ng build --aot=true&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;update-aug-2020&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update-aug-2020&quot; aria-label=&quot;update aug 2020 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update Aug 2020&lt;/h2&gt;
&lt;p&gt;My friend &lt;a href=&quot;https://github.com/nartc&quot;&gt;@nartc&lt;/a&gt; Just released an @angular schematics to add &lt;code class=&quot;language-text&quot;&gt;@tailwindcss&lt;/code&gt; to your @angular/cli projects.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setup Custom Webpack&lt;/li&gt;
&lt;li&gt;Enable &lt;code class=&quot;language-text&quot;&gt;purge&lt;/code&gt; for TS and HTML files&lt;/li&gt;
&lt;li&gt;Update &lt;code class=&quot;language-text&quot;&gt;angular.json&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;styles.scss&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Support Nx Workspace&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;usage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usage&quot; aria-label=&quot;usage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Usage&lt;/h3&gt;
&lt;p&gt;Simply run a single command on your Angular CLI project, and you are all set!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ng &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; @ngneat/tailwind&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See more ➡ &lt;a href=&quot;https://github.com/ngneat/tailwind&quot;&gt;https://github.com/ngneat/tailwind&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Everyone has a different way of organizing and working with CSS. I always encounter some use case where I need to set a simple &lt;code class=&quot;language-text&quot;&gt;padding-left: 5px&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;margin: 0 auto&lt;/code&gt;, or to make an element to have &lt;code class=&quot;language-text&quot;&gt;cursor: pointer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I have a few options for doing so.&lt;/p&gt;
&lt;h3 id=&quot;1-quick-and-dirty-way-inline-style&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-quick-and-dirty-way-inline-style&quot; aria-label=&quot;1 quick and dirty way inline style permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Quick and dirty way: inline style&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Submit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-create-a-new-class-for-each-element-such-as&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-create-a-new-class-for-each-element-such-as&quot; aria-label=&quot;2 create a new class for each element such as permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Create a new class for each element, such as&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;my-button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Submit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;fa fa-help my-icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And write a corresponding CSS for them.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.my-button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.my-icon&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Because Angular has &lt;a href=&quot;https://angular.io/api/core/ViewEncapsulation&quot;&gt;encapsulated the stylesheet&lt;/a&gt;, it will ensure that these classes will be available only in the component itself. I don’t have to worry about polluted the global CSS.&lt;/p&gt;
&lt;h3 id=&quot;3-create-a-common-css-on-a-top-level-component&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-create-a-common-css-on-a-top-level-component&quot; aria-label=&quot;3 create a common css on a top level component permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Create a common CSS on a top-level component&lt;/h3&gt;
&lt;p&gt;Suppose you see they are &lt;u&gt;repeating again and again&lt;/u&gt; on a different component. In that case, I could also bring all of that classes into &lt;code class=&quot;language-text&quot;&gt;app.component.scss&lt;/code&gt; and set the &lt;code class=&quot;language-text&quot;&gt;ViewEncapsulation.None&lt;/code&gt; to use in all the other components. But, the class name has to be changed because Those classes will be reused in many places.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.pl-1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.icon-active&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If I need a &lt;code class=&quot;language-text&quot;&gt;padding-left: 10px&lt;/code&gt;, I can just add new class &lt;code class=&quot;language-text&quot;&gt;.pl-2 { padding-left: 10px }&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;app.component.scss&lt;/code&gt;. In real life I will use SCSS loop to generate them, but let assume I do it manually for now.&lt;/p&gt;
&lt;p&gt;It is convenience, but &lt;u&gt;many problems&lt;/u&gt; arise with this approach&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to document these comment CSS so that all the team member can use.&lt;/li&gt;
&lt;li&gt;If your team member needs a style that doesn’t have a corresponding class, and he adds a new one that doesn’t follow your team standard. People might not know about that. It might also create duplication in the future.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s when you come to use &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;TailwindCSS&lt;/a&gt;. I have been using it quite sometimes, and it is &lt;u&gt;amazing&lt;/u&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TailwindCSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs without any annoying opinionated styles you have to fight to override.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In short, TailwindCSS create all the utility class for you. What you need to do is to spend about half an hour trying TailwindCSS, know some standard classes and syntax. You are good to go.&lt;/p&gt;
&lt;p&gt;If you’re reading this and you don’t know what TailwindCSS is, where have you been?&lt;/p&gt;
&lt;h2 id=&quot;how-to-configure-tailwindcss-with-angular-cli&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-configure-tailwindcss-with-angular-cli&quot; aria-label=&quot;how to configure tailwindcss with angular cli permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to configure TailwindCSS with Angular CLI&lt;/h2&gt;
&lt;h3 id=&quot;known-issues&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#known-issues&quot; aria-label=&quot;known issues permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Known issues&lt;/h3&gt;
&lt;p&gt;⚠️ &lt;strong&gt;The below manual approach has several issues&lt;/strong&gt; recently due to incompatible with the new breaking changes from &lt;code class=&quot;language-text&quot;&gt;postcss&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt;. Proceed with our own risk 😂&lt;/p&gt;
&lt;p&gt;Some known issues that I have received from people.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Schema validation failed with the following errors:
Data path &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; should NOT have additional properties&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;customWebpackConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 618px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/03e8c02b3e0e6d87b48738a82cd7b148/6e6fb/issue01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAABIUlEQVQY01WPTUsDMRiE9yyoFw9Kk7zZfHS32SRrkk12290uBWsrFkG8qBQ/zv7/H6At9ODDMMxlYCYzxvR936Y0DEOMsQmha1vvXBNCEwLnHADof8iJrLJ2WK3uthuXUmWtqq2+ra1ztfdN14a2jV3nYuNCiDGGEObzOeccY3woRzlNxjRKBUoLhDXCBqiGXAE1kFugJQEFQDG5RugGoWuEJgRPyKGdrZn8puKdymdZfdb+x8WvmX7h0zdRfIhyL8pXWexl+cjlioknMd1xec/ElssSaNYNQ3dx6a/EqNaLfhFTUrqilOZ5zhijjMHxHiWEEeAAnEBODk4JycZxTGfnQRbbzcNut1sul3VdG2tnSnnvtdboeA8Tgo6anMLf7F9ROjxA9IGtZAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular and why you should use it&quot;
        title=&quot;How to configure TailwindCSS with Angular and why you should use it&quot;
        src=&quot;/static/03e8c02b3e0e6d87b48738a82cd7b148/6e6fb/issue01.png&quot;
        srcset=&quot;/static/03e8c02b3e0e6d87b48738a82cd7b148/8ff5a/issue01.png 240w,
/static/03e8c02b3e0e6d87b48738a82cd7b148/e85cb/issue01.png 480w,
/static/03e8c02b3e0e6d87b48738a82cd7b148/6e6fb/issue01.png 618w&quot;
        sizes=&quot;(max-width: 618px) 100vw, 618px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can fix it by remove the &lt;code class=&quot;language-text&quot;&gt;customWebpackConfig&lt;/code&gt; from the builder for serve.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 639px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9a7f9ee73b35078fae3465975f857dcd/738b8/issue01-fix.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.416666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+ElEQVQY013KXUvCYACG4UUwcbo3daLBPnXv9m4TTdCWRohnddh5f0DT0rM+MDH64Xc4oYMOLni4eTSVpvhhiBMEeFFEejujN50RqgwlO/SUJJYdkliSJhFKSRIVkSUxWRoXLY5PLQi7aAMZMfB8hrZLv33J9GrMfP7ANL9j0h8yUSmjKCEPJWPHZWQ75J7Hje8XjvvaPRnaDtpjs8XCqLHTDD7ODD7Pq+yCPvtswpeX8e0n/HQzDi2Xg2mxL9eK35tW5v2PUTh27b7V5klYrHTBQhesK3XWls1StFnpVTZmg61o8FKps7lo8mpaPJcES12wLP2jC34Bae947HjrLSgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular and why you should use it&quot;
        title=&quot;How to configure TailwindCSS with Angular and why you should use it&quot;
        src=&quot;/static/9a7f9ee73b35078fae3465975f857dcd/738b8/issue01-fix.png&quot;
        srcset=&quot;/static/9a7f9ee73b35078fae3465975f857dcd/8ff5a/issue01-fix.png 240w,
/static/9a7f9ee73b35078fae3465975f857dcd/e85cb/issue01-fix.png 480w,
/static/9a7f9ee73b35078fae3465975f857dcd/738b8/issue01-fix.png 639w&quot;
        sizes=&quot;(max-width: 639px) 100vw, 639px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But after doing it, we faced another issue below, which I was looking for online. People only mentioned about the mismatched version of CLI and TypeScript and the like. I have no idea how to solve it. If you find the solution, please leave your comment 🤣&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;An unhandled exception occurred: Cannot &lt;span class=&quot;token builtin class-name&quot;&gt;read&lt;/span&gt; property &lt;span class=&quot;token string&quot;&gt;&apos;flags&apos;&lt;/span&gt; of undefined&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 915px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b7e5527938ef929e8d2112e29937e82/4255a/issue02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.666666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAABB0lEQVQY022Py26DMBBF+YFuKyXEY4/HYINjwOYRoERKm7QkfSy66f9/SkX6UBe9Gl3dxczVmagK4W6/73Z96b0Pwdd1Vdc6y1JjMmuRKGYsBmDwj6Imt4e68YkqURakfJI2WodUt9o02lSkLBeVQAkcAMSf4QDRKUlf1/huwkfTzTrbC5yRjkizVE+oTlI9UvKm0hFVJTCgLBfHDmUhMJp8GW7Wx/pyOD0gEeOcS7nhsLrSxrD4Ghhj3+C//Awgui+r3e1qfn65nM95ltk8L5xTRILz68r1jDEhxJI2m9+W5edWmxDH7dB3XTcMwzRN4zi2bdv3vbXWGOOcK3603W6dc0T0VfEJbFk4eoWVjtMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular and why you should use it&quot;
        title=&quot;How to configure TailwindCSS with Angular and why you should use it&quot;
        src=&quot;/static/9b7e5527938ef929e8d2112e29937e82/4255a/issue02.png&quot;
        srcset=&quot;/static/9b7e5527938ef929e8d2112e29937e82/8ff5a/issue02.png 240w,
/static/9b7e5527938ef929e8d2112e29937e82/e85cb/issue02.png 480w,
/static/9b7e5527938ef929e8d2112e29937e82/4255a/issue02.png 915w&quot;
        sizes=&quot;(max-width: 915px) 100vw, 915px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So I highly recommend you use &lt;a href=&quot;https://github.com/ngneat/tailwind&quot;&gt;ngneat/tailwind&lt;/a&gt; that I mentioned above for installing TailwindCSS into your Angular application.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;If you are not using CLI and have your own webpack configuration. It is still applicable, though&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I have prepared a simple application while writing this blog post. &lt;a href=&quot;https://github.com/trungvose/angular-tailwind-css-configuration&quot;&gt;View the source code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Suppose you have already had an Angular application create with Angular CLI. Follow those below steps.&lt;/p&gt;
&lt;h3 id=&quot;1-open-the-command-line-and-go-to-the-angular-application-folder&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-open-the-command-line-and-go-to-the-angular-application-folder&quot; aria-label=&quot;1 open the command line and go to the angular application folder permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Open the command line and go to the Angular application folder&lt;/h3&gt;
&lt;p&gt;On Windows, simple type &lt;code class=&quot;language-text&quot;&gt;cd path/to/your/folder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 652px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/16b831df65defe9ec79a962b823c7f55/dba9a/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.916666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAArUlEQVQY02XPS26DQBBFUdYBXQUNssOv6S8yg2T/y7qRIZGjeHCkepOSbmXjTLevtNsH7h6JfcZ7S9ktPlhybinF0PdC0ygi/5jfW1ARKn8U3JFZcmBPD9KciGnl8yvyOAIh3JgmwzAI1r503eW1FeksVT0P1NuNerTc24lVt/OB22rGqcaHhpQMuRhSNjgn515XOZXy3A3jYmmWSKWqqFxE5Yeifzxz3lLfXMnfpVtxvypvHp8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular&quot;
        title=&quot;How to configure TailwindCSS with Angular&quot;
        src=&quot;/static/16b831df65defe9ec79a962b823c7f55/dba9a/01.png&quot;
        srcset=&quot;/static/16b831df65defe9ec79a962b823c7f55/8ff5a/01.png 240w,
/static/16b831df65defe9ec79a962b823c7f55/e85cb/01.png 480w,
/static/16b831df65defe9ec79a962b823c7f55/dba9a/01.png 652w&quot;
        sizes=&quot;(max-width: 652px) 100vw, 652px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-install-tailwindcss-package-and-dependencies-needed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-install-tailwindcss-package-and-dependencies-needed&quot; aria-label=&quot;2 install tailwindcss package and dependencies needed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Install TailwindCSS package and dependencies needed&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i tailwindcss postcss-scss postcss-import postcss-loader @angular-builders/custom-webpack &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We will need &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack&lt;/code&gt; for customizing the build process of Angular CLI by overriding some of the webpack configurations.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 869px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f941cd761814ed3519d8718cbd6b6f42/2b2c6/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7EAAAOxAGVKw4bAAABDUlEQVQoz31Q7W6DMBDjPUoJLZR8ckkgAVq2vP9beUrWoq2a9sM63+nis1P1TqNbCXy2mIaI4Dnm2GFZbtj3AXHpsW4c97tEXDj2/QYbRvBpBeccTdPgfD4ftTLawJGFdx5+dnB+LvBTQIwbiDyE1NCaIISCNhZaG9BoYMaxiLZte4hW1jmEGPCREryfcL1e0fUd+r7HMNxK37AGjDGwozI0z1r4D5dViBGfKSGlBCFEGealvHA6nVDXdeH1D/4XjsjLumHdvjGHgLisuD8eILIljlQKQkoopUqf8TqYRV44HBpjSlSytggSUXGaH+W/yWCsxeVyefLfEd9RCSXBlQTRWNzk4X/R3iO+C34BRorJ2ZZW7GIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular&quot;
        title=&quot;How to configure TailwindCSS with Angular&quot;
        src=&quot;/static/f941cd761814ed3519d8718cbd6b6f42/2b2c6/02.png&quot;
        srcset=&quot;/static/f941cd761814ed3519d8718cbd6b6f42/8ff5a/02.png 240w,
/static/f941cd761814ed3519d8718cbd6b6f42/e85cb/02.png 480w,
/static/f941cd761814ed3519d8718cbd6b6f42/2b2c6/02.png 869w&quot;
        sizes=&quot;(max-width: 869px) 100vw, 869px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-import-tailwind-css-to-your-stylescss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-import-tailwind-css-to-your-stylescss&quot; aria-label=&quot;3 import tailwind css to your stylescss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Import Tailwind CSS to your style.scss&lt;/h3&gt;
&lt;p&gt;Next, you need to add the following to the top of the &lt;code class=&quot;language-text&quot;&gt;/src/style.scss&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tailwindcss/base&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tailwindcss/components&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tailwindcss/utilities&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;details&gt;
&lt;summary&gt;View the screenshot&lt;/summary&gt;
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 393px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/5a5a58141535577eecd997530ce6142b/d72ec/03.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 33.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABRUlEQVQoz4WQXW6DMBAGOUgwv7bBEIMNOAGSkkiR2vtfaKrQNmqf+jD6dqXd1Wijo7Fs20YIAWs76roizwtkqShLSVnIr/wHKdVO5LsG5zxd19O2LVprpFIo9ZVVVe+1rqp9oZQSJRWV1Ghd7fNKKYqi2EWij7vHNJbRjzjrOJ/OeNfjOktnW07TSBgHwjRg24a2MVhjMLnC9j3eOay1ZGnKIT4QJWlGkgomM3LrN05NQGY5Q2fZwsh19Jy9J9gjc1vhTUWR58TxgUTExPFfoqfm82BoJh7Dg0u3oouS8zTxuF54rAtvy8oyeOZjzXvo2fyR2dZ0jSFNU4QQL6IsyxCJYDCem7+x2BmZl5ymwP3yxn29cF2vLOPE2GimtmZ+/t1onOv3I0mSvIheukLsP/jpRZojsgKRfWeacYjjF/vML7Mfw0+e288l/oMbUQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;How to configure TailwindCSS with Angular&quot; title=&quot;How to configure TailwindCSS with Angular&quot; src=&quot;/static/5a5a58141535577eecd997530ce6142b/d72ec/03.png&quot; srcset=&quot;/static/5a5a58141535577eecd997530ce6142b/8ff5a/03.png 240w,
/static/5a5a58141535577eecd997530ce6142b/d72ec/03.png 393w&quot; sizes=&quot;(max-width: 393px) 100vw, 393px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;/details&gt;
&lt;h3 id=&quot;4-create-a-webpackconfigjs-file-at-the-root-of-your-project&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-create-a-webpackconfigjs-file-at-the-root-of-your-project&quot; aria-label=&quot;4 create a webpackconfigjs file at the root of your project permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Create a webpack.config.js file at the root of your project&lt;/h3&gt;
&lt;h4 id=&quot;this-is-what-it-should-look-like-for-postcss-loader30&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#this-is-what-it-should-look-like-for-postcss-loader30&quot; aria-label=&quot;this is what it should look like for postcss loader30 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;This is what it should look like for &lt;code class=&quot;language-text&quot;&gt;postcss-loader@^3.0&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.scss$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss-scss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token function-variable function&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;postcss-import&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tailwindcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;autoprefixer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;if-you-are-using-postcss-loader40-take-this-config-instead&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#if-you-are-using-postcss-loader40-take-this-config-instead&quot; aria-label=&quot;if you are using postcss loader40 take this config instead permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;If you are using &lt;code class=&quot;language-text&quot;&gt;postcss-loader@^4.0&lt;/code&gt;, take this config instead&lt;/h4&gt;
&lt;p&gt;Noted there is additional &lt;code class=&quot;language-text&quot;&gt;postcssOptions&lt;/code&gt; property inside options. See the detail from &lt;a href=&quot;https://webpack.js.org/loaders/postcss-loader/&quot;&gt;its documentation&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.scss$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;postcssOptions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;ident&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;postcss-scss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;postcss-import&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tailwindcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;autoprefixer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;I have some notes on &lt;code class=&quot;language-text&quot;&gt;Tailwind bundle size&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;postcss-scss&lt;/code&gt; for you&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;controlling-file-size-with-tailwind&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#controlling-file-size-with-tailwind&quot; aria-label=&quot;controlling file size with tailwind permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Controlling file size with Tailwind&lt;/h4&gt;
&lt;p&gt;When using the default configuration, Tailwind CSS’s development build is &lt;strong&gt;1996kb&lt;/strong&gt; uncompressed, &lt;strong&gt;144.6kb&lt;/strong&gt; minified and compressed with Gzip, and &lt;strong&gt;37.kb&lt;/strong&gt; when compressed with Brotli.&lt;/p&gt;
&lt;p&gt;When building for production, you should always use Tailwind’s purge option to tree-shake unused styles and optimize your final build size. When removing unused styles with Tailwind, it’s tough to end up with more than 10kb of compressed CSS.&lt;/p&gt;
&lt;p&gt;To enable purge, simply add this option in your &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// tailwind.config.js&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;purge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./src/**/*.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./src/**/*.ts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more on that, &lt;a href=&quot;https://tailwindcss.com/docs/controlling-file-size/#setting-up-purgecss&quot;&gt;view Tailwind documentation&lt;/a&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;View the screenshot - See the CSS bundle size different between &lt;u&gt;1 MB&lt;/u&gt; and &lt;u&gt;2 KB&lt;/u&gt;&lt;/summary&gt;
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/a97ce402d3773be8ef12b3609a094bfd/21b4d/10.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 15%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAuUlEQVQI1xXK0U6DMBiAUR5kfxFGaRkUbCkUxtgwxgsXo+//NJ/Z7cnJ6qHFuI6mMFitaJucy0VR1wpjcqpSEFFUreV1bakxNscYwTYndC3oSmjsiVxrshhH3OzRk6MMHfrd0bU9fmhwwfLWGpRSLCniPkbOXwPynZDD04VAip51ryn2HjkXZNM8sqYXRqbNs6fAM638LB2fN8fQN+SiGEfPvk1cj8TyiNxvM7/bnb+r53n0hNijRPgHGjlb1/enl5wAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;How to configure TailwindCSS with Angular&quot; title=&quot;How to configure TailwindCSS with Angular&quot; src=&quot;/static/a97ce402d3773be8ef12b3609a094bfd/d9199/10.png&quot; srcset=&quot;/static/a97ce402d3773be8ef12b3609a094bfd/8ff5a/10.png 240w,
/static/a97ce402d3773be8ef12b3609a094bfd/e85cb/10.png 480w,
/static/a97ce402d3773be8ef12b3609a094bfd/d9199/10.png 960w,
/static/a97ce402d3773be8ef12b3609a094bfd/21b4d/10.png 1280w&quot; sizes=&quot;(max-width: 960px) 100vw, 960px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;/details&gt;
&lt;h4 id=&quot;postcss-scss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#postcss-scss&quot; aria-label=&quot;postcss scss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;postcss-scss&lt;/h4&gt;
&lt;p&gt;It will &lt;u&gt;not compile SCSS&lt;/u&gt;. It simply parses mixins as custom at-rules &amp;#x26; &lt;code class=&quot;language-text&quot;&gt;variables&lt;/code&gt; as properties so that PostCSS plugins can then transform SCSS source code alongside CSS. Mixin and for loop won’t be transformed. You have to use &lt;code class=&quot;language-text&quot;&gt;sass-loader&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;less-loader&lt;/code&gt; if you need to compile your SASS/LESS code into CSS, then do the &lt;code class=&quot;language-text&quot;&gt;postcss-loader&lt;/code&gt; on top of that.&lt;/p&gt;
&lt;p&gt;See my &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/webpack.config.js&quot;&gt;webpack.config.js&lt;/a&gt; for project &lt;a href=&quot;https://jira.trungk18.com/&quot;&gt;jira-clone-angular&lt;/a&gt; that use additional &lt;code class=&quot;language-text&quot;&gt;sass-loader&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;But you might ask, &lt;a href=&quot;https://hashnode.com/post/difference-of-postcss-and-scss-cjaw5cm0f02nuxmwtl4qrk5sj&quot;&gt;what is different between SCSS and PostCSS&lt;/a&gt;?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PostCSS is the closest thing in the CSS world to what ‘Babel’ is in the JavaScript world - it parses CSS, loads plugins that apply transformations to your code, and manages these transformations. SCSS is a preprocessor and can be loaded by PostCSS, but PostCSS can load a lot more than just SCSS :D With PostCSS you could manage SCSS and Less and Stylus code, plus other things - all in the same codebase together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I still didn’t really get what is PostCSS 😂 &lt;strong&gt;In short, To make SCSS/LESS work with PostCSS, please config your &lt;code class=&quot;language-text&quot;&gt;webpack.config.js&lt;/code&gt; with an additionally needed loader &lt;a href=&quot;https://github.com/trungvose/jira-clone-angular/blob/master/frontend/webpack.config.js&quot;&gt;I mentioned above&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;View the screenshot&lt;/summary&gt;
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 580px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/88ba6ed65b96125b0233a870dffa8222/b6272/04.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 71.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABvklEQVQ4y52T2W7bMBBF/R8WOaS4ipIsyUvixEiXFH3t///NKSQ7TtC6RtCHqyEgzuGdGXI1bvc8n04ctjuOhwO7aSJ6T4yZGBNNacltT4qJlDIxZbwPOOdvatU3jqFvKdNE/7in3Y3kaWCcBrqSKN3AcdzznBOPOfPYNPi6RimFVmqJH7VSWmPEYHOme/lO3h6oY0KMRrSiqirsek2lFDKvq+qcrPVtoIimdjVqXRGblm7cImIuG/SSWM3xAllAd7SaP9ZaRASt1oScCaUnWmHKkTYFrAi1kXPSXOob+LL+C1jX9QJUVUUe9hx+/qLterY50QRHnwIluDPoBuTfDud+1o724YXYbtjMsOhw9fxfX4H3tDLGEEO4niDG8PT1C/vnI10pjJsNJQaSe2uLQu45nAfg/bmcpWyl6drI6TQxDZmpKwylYWoSQ+OordAZQ2cNgzWM1hCN0BqDnYGzwxD8FVhVitJmXl+PnB4avh22/Dg+8LKb2BS7DEP+kP4QF6D3/mrZWofzAWNk6ZuR90TziT5eHIbzyUaTlvLl/R7eSLo75beS540h5sXZ/Do+M9E7Dj3BW7x1Z6ci/63fOBWHsAQ/biYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;How to configure TailwindCSS with Angular&quot; title=&quot;How to configure TailwindCSS with Angular&quot; src=&quot;/static/88ba6ed65b96125b0233a870dffa8222/b6272/04.png&quot; srcset=&quot;/static/88ba6ed65b96125b0233a870dffa8222/8ff5a/04.png 240w,
/static/88ba6ed65b96125b0233a870dffa8222/e85cb/04.png 480w,
/static/88ba6ed65b96125b0233a870dffa8222/b6272/04.png 580w&quot; sizes=&quot;(max-width: 580px) 100vw, 580px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;/details&gt;
&lt;h3 id=&quot;5-modify-the-angularjson-file-to-use-the-custom-builder-and-the-webpack-config-file&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-modify-the-angularjson-file-to-use-the-custom-builder-and-the-webpack-config-file&quot; aria-label=&quot;5 modify the angularjson file to use the custom builder and the webpack config file permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Modify the angular.json file to use the custom builder and the webpack config file&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;architect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;builder&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@angular-builders/custom-webpack:browser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;options&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;customWebpackConfig&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./webpack.config.js&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;serve&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;builder&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@angular-builders/custom-webpack:dev-server&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;options&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;customWebpackConfig&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./webpack.config.js&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;, see the &lt;code class=&quot;language-text&quot;&gt;&quot;builder&quot;&lt;/code&gt; has been changed from &lt;code class=&quot;language-text&quot;&gt;@angular-devkit/build-angular:dev-server&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;@angular-builders/custom-webpack:browser&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/27381cd865dabd1699d5862df885d809/0d1a4/05.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.333333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAuUlEQVQI1y3Fa06DQBSAURbCDMzrKhSQ0pRMjX2BadVEEY2JTdz/Nj5j7I+TkyzrliJLudWaSikKraiVotQK0YqQpnilcN5ws6hoNzvKQjC5wuQa7w1BDCIG73KSuIqsJdAHx4OzRGfZekv0ls5ZGp1Rm4yuX9DflwzvZ4bpifEUOYxrhqHmODYcHxt2h4rEi+D+BMHKP3PdXUkhbPY1z3PHdIm8/ux5+47Ml8j0teLlc8lpvuP80fILBR9cxAG89qUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular&quot;
        title=&quot;How to configure TailwindCSS with Angular&quot;
        src=&quot;/static/27381cd865dabd1699d5862df885d809/d9199/05.png&quot;
        srcset=&quot;/static/27381cd865dabd1699d5862df885d809/8ff5a/05.png 240w,
/static/27381cd865dabd1699d5862df885d809/e85cb/05.png 480w,
/static/27381cd865dabd1699d5862df885d809/d9199/05.png 960w,
/static/27381cd865dabd1699d5862df885d809/0d1a4/05.png 1036w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;6-we-are-finished&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-we-are-finished&quot; aria-label=&quot;6 we are finished permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. We are finished&lt;/h3&gt;
&lt;p&gt;Run &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; and start adding some Tailwind classes to see if it is working.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a2d91a9839eaaae3c95726556a392ced/d5b59/06.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACJ0lEQVQoz32Q204TURSG+wDGCGoowhxa2ulMpzN7pp0pBUo1UeIL+AYiRGPCDaIEEfFGTMRAI7wakUtbDrEgcrDt0MJnOg0J0cSdfPmz/qy19sofyRdGyXs5PM/DtgWWLUIVwsEWTqjZbK7r2QLTtPD8Ao7rIZwspuXgFwqUiuN4+QKRrOui6zqWk0Vk0jgZE5E2SGkaiWSHJEktharGiEb7GS+V+LD8noWFt8zPv2Fu7jWzs69YfrfI0tIyETs3jBRPkBY58o7NiGszknUouCKsTV3H0JKkkgn6olEmn02ztbXFt+1tarUDjo5+sru3x/7+PkEQEHG8PIqiYFoued9HGBqZVBKha1i6RkYbwurUaYO+u3eYmnrB6Umbw8M6QcA/L+LmfFRFQTcFRtokriqoiowkScgdZDmk09MfjTIx8Zi19U1WPq3ycWWVz1/KrJU3WC9vUv660c0wFo+T0DOhdgaVEDXUq4UdOp8Yhk6xOIbnD+PnfTKWjZf3efSgRKEwSkQIETZecX3BdVRVpbf3NpOTTzk4qFGpVNjZqbK7u0Ple4VqtRoSyVgWUjikIIXIf9H11VicWz09zMzMhFnV6w2C8xbNIKDVvqAZtLoZ2qaBdC+KPND/X2LyID03b/B8eooL4NdZg9pxgx/HDU7OGpzWA5rnbSLph08YsMeQnHEGRTFk4BpXnpy9z80hl5dzi+ElQavNye9zGkGboNng8vIivPAPkW+cOQHKQL4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular&quot;
        title=&quot;How to configure TailwindCSS with Angular&quot;
        src=&quot;/static/a2d91a9839eaaae3c95726556a392ced/d9199/06.png&quot;
        srcset=&quot;/static/a2d91a9839eaaae3c95726556a392ced/8ff5a/06.png 240w,
/static/a2d91a9839eaaae3c95726556a392ced/e85cb/06.png 480w,
/static/a2d91a9839eaaae3c95726556a392ced/d9199/06.png 960w,
/static/a2d91a9839eaaae3c95726556a392ced/d5b59/06.png 1370w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;configure-to-use-tailwindcss-effectively-in-vscode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configure-to-use-tailwindcss-effectively-in-vscode&quot; aria-label=&quot;configure to use tailwindcss effectively in vscode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Configure to use TailwindCSS effectively in VSCode&lt;/h2&gt;
&lt;p&gt;Tailwind is great. But with hundreds of utility classes might not be easy to remember. We can configure VSCode to give us all the suggestions for Tailwind.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/e7aaf411465f24a389717d262d0247c4/07.gif&quot; alt=&quot;How to configure TailwindCSS with Angular&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;To do so, follow these steps.&lt;/p&gt;
&lt;h3 id=&quot;1-install-tailwind-css-intellisense&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-install-tailwind-css-intellisense&quot; aria-label=&quot;1 install tailwind css intellisense permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Install Tailwind CSS IntelliSense&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss&quot;&gt;https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-create-a-tailwind-configuration-file&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-create-a-tailwind-configuration-file&quot; aria-label=&quot;2 create a tailwind configuration file permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Create a Tailwind configuration file&lt;/h3&gt;
&lt;p&gt;Simple run &lt;code class=&quot;language-text&quot;&gt;npx tailwind init&lt;/code&gt;. It will give you a default configuration file &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt;. This extension required this file to be able to work properly.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 651px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a18bff9f4233f2e30a4b8675fdefcbab/1ac66/08.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABZUlEQVQoz6WRWY7bMBBEdYxYZFMiqYXUQkm2ZM8CA5P7H+oFlo0ZD5KPIPl4qG420KguZtVxwL9M1NcTcUkc7cpQBY6rYhyF00nYNmF9sL+tetcxCeeLZpkVtu/R3UQWm8g8zExDomojqizI8wN5rlFKkyvF4UGuNFqbPyJaY7Qm86PnfL2wnjaaNFF3A01d0zQN3nuCC5+0rsEYgzHypPLZizFkOiqq5KhdTQgtbdsQYiTEjrpqGVxi8DcmOtsjcnMku95rg95rwYiQFaHGjAE5RcrYsLiV6BrG6QexU4zTgWk5kOYbOX0vzIvs2g+3PIWUFGWM6DCSHS8b69uFl+s787KwpCOVc1gnWGvwzuD9F6UV3D674/1dTVkipSV7fX3j4+MnXYwUpkCL3u1/O+k5fPnS7/Xj5DYE1m2jbVustffBb4H/DY9PSfPM+bzR9z3OuaeF/0Z2cxZCoKoqiqL474W/ALtPGYtksLGIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular&quot;
        title=&quot;How to configure TailwindCSS with Angular&quot;
        src=&quot;/static/a18bff9f4233f2e30a4b8675fdefcbab/1ac66/08.png&quot;
        srcset=&quot;/static/a18bff9f4233f2e30a4b8675fdefcbab/8ff5a/08.png 240w,
/static/a18bff9f4233f2e30a4b8675fdefcbab/e85cb/08.png 480w,
/static/a18bff9f4233f2e30a4b8675fdefcbab/1ac66/08.png 651w&quot;
        sizes=&quot;(max-width: 651px) 100vw, 651px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That’s all. You can now use the power of VSCode together with Tailwind, how sweet it is!&lt;/p&gt;
&lt;h2 id=&quot;using-tailwindcss-apply-syntax&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-tailwindcss-apply-syntax&quot; aria-label=&quot;using tailwindcss apply syntax permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using TailwindCSS @apply syntax&lt;/h2&gt;
&lt;p&gt;Tailwind provides a handy &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt; keyword. Think about If you have ten similar buttons on a sample page. You have to copy the HTML code ten times.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Submit
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you need to change the color, you also have to change 10 times, which is a nightmare.&lt;/p&gt;
&lt;p&gt;With &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt;, you can create a new class &lt;code class=&quot;language-text&quot;&gt;.btn&lt;/code&gt; and put all the style you want to reuse. Tailwind will transform our code below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn-tailwind&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Submit
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.btn-tailwind &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@apply&lt;/span&gt; bg-transparent text-blue-700 font-semibold py-2 px-4 border border-blue-500 rounded&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &amp;amp;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    @apply bg-blue-500 text-white border-transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/23f4829ba196a4edda580f7583b1323c/84bf8/09.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACR0lEQVQoz3WOz0/TYByHm3jxSGCbK7D+evu2e9u1a7u37dotoKgDz0QSxA4MGq7+RfJrGIInT8azf4EXY+LJgxdlJCxubdduXwPqifDkc33yeZig1fZd6nlu2PQ9zw/DVhCEf6Gu57l+M2i1lx56ftv1wgYNvGZ7eWWlvbzSXr7PUMfWiIpVomk1UiUIyaKE0NUkGauCIHY6q3t7r7a2om73+bPNzfX1je3taPflbnfnBWPRJi8IqqI4DrVrmkOwRRSbKFYVVzG6V5zbPzgAgNFolP4njpM0SeIkYZwGFZCsqlXTMLEkYiRhJCkIKbKEEJqZne2dnADAxcXF5eUgTtLhcDgcDQeDwWQyYWwvQBhLqi6qOkJIQaIsCTLGiqoSTZ8rlY+OewCQZRkAfP704eeP7wAwnU4BgLEblOM4jhc4jqtUFhWMXd+n1FWwqirVcpk9PDy6yk7SHODb1y/n/X4OEKdZmuUMbTjCNTwv8Dxf1U2b+o5V12uGaZqcIPdO3gLA5e9RfzA8H2W/BnF/MEyzfDKZMo5t2XXTc926aRBCLKvhUN+2aU03dN1g2YXedXaSjtMsS8bjOB3HafYv2zAMURScuuUFTcOhTviAtjuWGzaarYbXXFisnJ2eAkCe53ADxjDrIpI13dBqtlKziGlXdUNWCSY1QUKFYvHd2dmtskUUab6AF0oiW+TLBYktivMlni3ybElgCzN37xzvv7n9eeM139lBq5HwOEKrXdTpco8ieS1Sn0Robae89LT3/uO1PLkp/wEOOk/bAFGN3wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How to configure TailwindCSS with Angular&quot;
        title=&quot;How to configure TailwindCSS with Angular&quot;
        src=&quot;/static/23f4829ba196a4edda580f7583b1323c/d9199/09.png&quot;
        srcset=&quot;/static/23f4829ba196a4edda580f7583b1323c/8ff5a/09.png 240w,
/static/23f4829ba196a4edda580f7583b1323c/e85cb/09.png 480w,
/static/23f4829ba196a4edda580f7583b1323c/d9199/09.png 960w,
/static/23f4829ba196a4edda580f7583b1323c/84bf8/09.png 1162w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The output looks the same as we use classes. For more information on &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Intellisense doesn’t like the SCSS syntax. It gave the &lt;strong&gt;red error underline of the class&lt;/strong&gt; after using &lt;code class=&quot;language-text&quot;&gt;@apply&lt;/code&gt;. But the build work &lt;u&gt;perfectly fine&lt;/u&gt;. You should not worry about that. See more about that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/course/composing-utilities-with-apply/#app&quot;&gt;https://tailwindcss.com/course/composing-utilities-with-apply/#app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/docs/extracting-components/#app&quot;&gt;https://tailwindcss.com/docs/extracting-components/#app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/angular-tailwind-css-configuration&quot;&gt;https://github.com/trungvose/angular-tailwind-css-configuration&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;notes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#notes&quot; aria-label=&quot;notes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Notes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I have tested using Angular version &lt;code class=&quot;language-text&quot;&gt;&quot;@angular/core&quot;: &quot;~9.1.11&quot;&lt;/code&gt;. It is working with the previous version of Angular such as &lt;code class=&quot;language-text&quot;&gt;8.x.x&lt;/code&gt; as well.&lt;/li&gt;
&lt;li&gt;It is also &lt;u&gt;working&lt;/u&gt; with AOT when you do &lt;code class=&quot;language-text&quot;&gt;ng build --aot=true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It will increase your bundle size, you should always configure &lt;a href=&quot;https://tailwindcss.com/docs/controlling-file-size/#setting-up-purgecss&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;purge&lt;/code&gt;&lt;/a&gt; on &lt;code class=&quot;language-text&quot;&gt;tailwind.config.js&lt;/code&gt; for production build.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/seankerwin/angular-8-tailwind-css-guide-3m45&quot;&gt;A comprehensive guide of how to configure TailwindCSS&lt;/a&gt; - &lt;a href=&quot;https://dev.to/seankerwin&quot;&gt;@Sean Kerwin&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[NPM vs Bower vs Browserify vs Gulp vs Grunt vs Webpack]]></title><description><![CDATA[Repost my killer answer on StackOverflow with more than 600 votes]]></description><link>https://trungvose.comnpm-vs-bower-vs-browserify-vs-gulp-vs-grunt-vs-webpack/</link><guid isPermaLink="false">https://trungvose.comnpm-vs-bower-vs-browserify-vs-gulp-vs-grunt-vs-webpack/</guid><pubDate>Wed, 10 Jun 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Repost my killer answer on &lt;a href=&quot;https://stackoverflow.com/a/39825582/3375906&quot;&gt;stackoverflow&lt;/a&gt; with more than 600 votes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update October 2018&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are still uncertain about Front-end dev, you can take a quick look into an excellent resource here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/kamranahmedse/developer-roadmap&quot;&gt;https://github.com/kamranahmedse/developer-roadmap&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update June 2018&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Learning modern JavaScript is tough if you haven’t been there since the beginning. If you are the newcomer, remember to check this excellent written to have a better overview.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/the-node-js-collection/modern-javascript-explained-for-dinosaurs-f695e9747b70&quot;&gt;https://medium.com/the-node-js-collection/modern-javascript-explained-for-dinosaurs-f695e9747b70&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Update July 2017&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Recently I found a comprehensive guide from Grab team about how to approach front-end development in 2017. You can check it out as below.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/grab/front-end-guide&quot;&gt;https://github.com/grab/front-end-guide&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I’ve been also searching for this quite some time since there are a lot of tools out there and each of them benefits us in a different aspect. The community is divided across tools like &lt;code class=&quot;language-text&quot;&gt;Browserify, Webpack, jspm, Grunt and Gulp&lt;/code&gt;. You might also hear about &lt;code class=&quot;language-text&quot;&gt;Yeoman or Slush&lt;/code&gt;. That’s not a problem, it’s just confusing for everyone trying to understand a clear path forward.&lt;/p&gt;
&lt;p&gt;Anyway, I would like to contribute something.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-package-manager&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-package-manager&quot; aria-label=&quot;1 package manager permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Package Manager&lt;/h2&gt;
&lt;p&gt;Package managers simplify installing and updating project dependencies, which are libraries such as: &lt;code class=&quot;language-text&quot;&gt;jQuery, Bootstrap&lt;/code&gt;, etc - everything that is used on your site and isn’t written by you.&lt;/p&gt;
&lt;p&gt;Browsing all the library websites, downloading and unpacking the archives, copying files into the projects — all of this is replaced with a few commands in the terminal.&lt;/p&gt;
&lt;h3 id=&quot;npm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#npm&quot; aria-label=&quot;npm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.npmjs.com/&quot;&gt;NPM&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It stands for: &lt;code class=&quot;language-text&quot;&gt;Node JS package manager&lt;/code&gt; helps you to manage all the libraries your software relies on. You would define your needs in a file called &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; and run &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; in the command line… then BANG, your packages are downloaded and ready to use. It could be used both for &lt;code class=&quot;language-text&quot;&gt;front-end and back-end&lt;/code&gt; libraries.&lt;/p&gt;
&lt;h3 id=&quot;bower&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bower&quot; aria-label=&quot;bower permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://bower.io/&quot;&gt;Bower&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For front-end package management, the concept is the same with NPM. All your libraries are stored in a file named &lt;code class=&quot;language-text&quot;&gt;bower.json&lt;/code&gt; and then run &lt;code class=&quot;language-text&quot;&gt;bower install&lt;/code&gt; in the command line.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bower is recommended their user to &lt;a href=&quot;https://bower.io/blog/2017/how-to-migrate-away-from-bower/&quot;&gt;migrate over to npm or yarn&lt;/a&gt;. Please be careful&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;difference-between-bower-and-npm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#difference-between-bower-and-npm&quot; aria-label=&quot;difference between bower and npm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Difference between &lt;code class=&quot;language-text&quot;&gt;Bower&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;NPM&lt;/code&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The biggest difference between &lt;code class=&quot;language-text&quot;&gt;Bower&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;NPM&lt;/code&gt; is that NPM does nested
dependency tree while Bower requires a flat dependency tree as below.&lt;/p&gt;
&lt;p&gt;Quoting from &lt;a href=&quot;https://stackoverflow.com/questions/18641899/what-is-the-difference-between-bower-and-npm&quot;&gt;https://stackoverflow.com/questions/18641899/what-is-the-difference-between-bower-and-npm&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.npmjs.com/&quot;&gt;NPM&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;project root
[node_modules] // default directory for dependencies
 -&gt; dependency A
 -&gt; dependency B
    [node_modules]
    -&gt; dependency A

 -&gt; dependency C
    [node_modules]
    -&gt; dependency B
      [node_modules]
       -&gt; dependency A
    -&gt; dependency D&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://bower.io/&quot;&gt;Bower&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;project root
[bower_components] // default directory for dependencies
 -&gt; dependency A
 -&gt; dependency B // needs A
 -&gt; dependency C // needs B and D
 -&gt; dependency D&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;There are some updates on &lt;a href=&quot;https://docs.npmjs.com/how-npm-works/npm3-dupe&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm 3 Duplication and Deduplication&lt;/code&gt;&lt;/a&gt;,
please open the doc for more detail.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;yarn&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#yarn&quot; aria-label=&quot;yarn permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://yarnpkg.com/&quot;&gt;Yarn&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A new package manager for &lt;code class=&quot;language-text&quot;&gt;JavaScript&lt;/code&gt; &lt;a href=&quot;https://code.facebook.com/posts/1840075619545360&quot;&gt;published&lt;/a&gt; by &lt;code class=&quot;language-text&quot;&gt;Facebook&lt;/code&gt; recently with some more advantages compared to &lt;code class=&quot;language-text&quot;&gt;NPM&lt;/code&gt;. And with Yarn, you still can use both &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;NPM&lt;/code&gt;&lt;/a&gt;and &lt;a href=&quot;https://bower.io/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Bower&lt;/code&gt;&lt;/a&gt; registry to fetch the package. If you’ve installed a package before, &lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; creates a cached copy which facilitates &lt;code class=&quot;language-text&quot;&gt;offline package installs&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;jspm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jspm&quot; aria-label=&quot;jspm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://jspm.io/&quot;&gt;jspm&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;JSPM is a package manager for the &lt;code class=&quot;language-text&quot;&gt;SystemJS&lt;/code&gt; universal module loader, built on top of the dynamic &lt;code class=&quot;language-text&quot;&gt;ES6&lt;/code&gt; module loader. It is not an entirely new package manager with its own set of rules, rather it works on top of existing package sources. Out of the box, it works with &lt;code class=&quot;language-text&quot;&gt;GitHub&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;. As most of the &lt;code class=&quot;language-text&quot;&gt;Bower&lt;/code&gt; based packages are based on &lt;code class=&quot;language-text&quot;&gt;GitHub&lt;/code&gt;, we can install those packages using &lt;code class=&quot;language-text&quot;&gt;jspm&lt;/code&gt; as well. It has a registry that lists most of the commonly used front-end packages for easier installation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;See the different between &lt;a href=&quot;https://bower.io/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Bower&lt;/code&gt;&lt;/a&gt; and &lt;code class=&quot;language-text&quot;&gt;jspm&lt;/code&gt;:
&lt;a href=&quot;https://stackoverflow.com/questions/25416813/package-manager-bower-vs-jspm&quot;&gt;https://stackoverflow.com/questions/25416813/package-manager-bower-vs-jspm&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-module-loaderbundling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-module-loaderbundling&quot; aria-label=&quot;2 module loaderbundling permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Module Loader/Bundling&lt;/h2&gt;
&lt;p&gt;Most projects of any scale will have their code split between several files. You can just include each file with an individual &lt;code class=&quot;language-text&quot;&gt;&amp;lt;script&gt;&lt;/code&gt; tag, however, &lt;code class=&quot;language-text&quot;&gt;&amp;lt;script&gt;&lt;/code&gt; establishes a new HTTP connection, and for small files – which is a goal of modularity – the time to set up the connection can take significantly longer than transferring the data. While the scripts are downloading, no content can be changed on the page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The problem of download time can largely be solved by concatenating a group of simple modules into a single file and minifying it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;E.g&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Wagon&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;“build/wagon-bundle.js”&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The performance comes at the expense of flexibility though. If your modules have inter-dependency, this lack of flexibility may be a showstopper.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;E.g&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Skateboard&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;“connectors/axle.js”&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;“frames/board.js”&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- skateboard-wheel and ball-bearing both depend on abstract-rolling-thing --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;“rolling-things/abstract-rolling-thing.js”&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;“rolling-things/wheels/skateboard-wheel.js”&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- but if skateboard-wheel also depends on ball-bearing --&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- then having this script tag here could cause a problem --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;“rolling-things/ball-bearing.js”&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- connect wheels to axle and axle to frame --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;“vehicles/skateboard/our-sk8bd-init.js”&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Computers can do that better than you can, and that is why you should use a tool to automatically bundle everything into a single file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Then we heard about &lt;code class=&quot;language-text&quot;&gt;RequireJS&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Browserify&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Webpack&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;SystemJS&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;requirejs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#requirejs&quot; aria-label=&quot;requirejs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://requirejs.org/&quot;&gt;RequireJS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is a &lt;code class=&quot;language-text&quot;&gt;JavaScript&lt;/code&gt; file and module loader. It is optimized for in-browser use, but it can be used in other JavaScript environments, like &lt;code class=&quot;language-text&quot;&gt;Node&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;E.g: &lt;strong&gt;myModule.js&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// package/lib is a dependency we require&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;package/lib&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// behavior for our module&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    lib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hello world!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// export (expose) foo to other modules as foobar&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;foobar&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In &lt;code class=&quot;language-text&quot;&gt;main.js&lt;/code&gt;, we can import &lt;code class=&quot;language-text&quot;&gt;myModule.js&lt;/code&gt; as a dependency and use it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;require([&quot;package/myModule&quot;], function(myModule) {
    myModule.foobar();
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then in our &lt;code class=&quot;language-text&quot;&gt;HTML&lt;/code&gt;, we can refer to use with &lt;code class=&quot;language-text&quot;&gt;RequireJS&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;script src=“app/require.js” data-main=“main.js” &gt;&amp;lt;/script&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Read more about &lt;code class=&quot;language-text&quot;&gt;CommonJS&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AMD&lt;/code&gt; to get understanding easily.
&lt;a href=&quot;https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs&quot;&gt;https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;browserify&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#browserify&quot; aria-label=&quot;browserify permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://browserify.org/&quot;&gt;Browserify&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Set out to allow the use of &lt;code class=&quot;language-text&quot;&gt;CommonJS&lt;/code&gt; formatted modules in the browser. Consequently, &lt;code class=&quot;language-text&quot;&gt;Browserify&lt;/code&gt; isn’t as much a module loader as a module bundler: &lt;code class=&quot;language-text&quot;&gt;Browserify&lt;/code&gt; is entirely a build-time tool, producing a bundle of code that can then be loaded client-side.&lt;/p&gt;
&lt;p&gt;Start with a build machine that has node &amp;#x26; npm installed, and get the package:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm install -g –save-dev browserify&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Write your modules in &lt;code class=&quot;language-text&quot;&gt;CommonJS&lt;/code&gt; format&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//entry-point.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;../foo.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And when happy, issue the command to bundle:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;browserify entry-point.js -o bundle-name.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Browserify recursively finds all dependencies of entry-point and assembles them into a single file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;”bundle-name.js”&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;webpack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webpack&quot; aria-label=&quot;webpack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://webpack.github.io/&quot;&gt;Webpack&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It bundles all of your static assets, including &lt;code class=&quot;language-text&quot;&gt;JavaScript&lt;/code&gt;, images, CSS, and more, into a single file. It also enables you to process the files through different types of loaders. You could write your &lt;code class=&quot;language-text&quot;&gt;JavaScript&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;CommonJS&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;AMD&lt;/code&gt; modules syntax. It attacks the build problem in a fundamentally more integrated and opinionated manner. In &lt;code class=&quot;language-text&quot;&gt;Browserify&lt;/code&gt; you use &lt;code class=&quot;language-text&quot;&gt;Gulp/Grunt&lt;/code&gt; and a long list of transforms and plugins to get the job done. &lt;code class=&quot;language-text&quot;&gt;Webpack&lt;/code&gt; offers enough power out of the box that you typically don’t need &lt;code class=&quot;language-text&quot;&gt;Grunt&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Gulp&lt;/code&gt; at all.&lt;/p&gt;
&lt;p&gt;Basic usage is beyond simple. Install Webpack like Browserify:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm install -g –save-dev webpack&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And pass the command an entry point and an output file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;webpack ./entry-point.js bundle-name.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;systemjs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#systemjs&quot; aria-label=&quot;systemjs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/systemjs/systemjs&quot;&gt;SystemJS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is a module loader that &lt;strong&gt;can import modules at run time in any of the popular formats&lt;/strong&gt; used today (&lt;code class=&quot;language-text&quot;&gt;CommonJS, UMD, AMD, ES6&lt;/code&gt;). It is built on top of the &lt;code class=&quot;language-text&quot;&gt;ES6&lt;/code&gt; module loader polyfill and is smart enough to detect the format being used and handle it appropriately. &lt;code class=&quot;language-text&quot;&gt;SystemJS&lt;/code&gt; can also transpile ES6 code (with &lt;code class=&quot;language-text&quot;&gt;Babel&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Traceur&lt;/code&gt;) or other languages such as &lt;code class=&quot;language-text&quot;&gt;TypeScript&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;CoffeeScript&lt;/code&gt; using plugins.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Want to know what is the &lt;code class=&quot;language-text&quot;&gt;node module&lt;/code&gt; and why it is not well adapted to in-browser.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;More useful article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@housecor/browserify-vs-webpack-b3d7ca08a0a9#.c1q7ao3h4&quot;&gt;https://medium.com/@housecor/browserify-vs-webpack-b3d7ca08a0a9#.c1q7ao3h4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://jamesknelson.com/which-build-system-should-i-use-for-my-javascript-app/&quot;&gt;http://jamesknelson.com/which-build-system-should-i-use-for-my-javascript-app/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://appendto.com/2016/06/the-short-history-of-javascript-module-loaders/&quot;&gt;https://appendto.com/2016/06/the-short-history-of-javascript-module-loaders/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;Why &lt;code class=&quot;language-text&quot;&gt;jspm&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;SystemJS&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;One of the main goals of &lt;code class=&quot;language-text&quot;&gt;ES6&lt;/code&gt; modularity is to make it really simple
to install and use any Javascript library from anywhere on the
Internet (&lt;code class=&quot;language-text&quot;&gt;Github&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;, etc.). Only two things are needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A single command to install the library&lt;/li&gt;
&lt;li&gt;One single line of code to import the library and use it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So with &lt;code class=&quot;language-text&quot;&gt;jspm&lt;/code&gt;, you can do it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the library with a command: &lt;code class=&quot;language-text&quot;&gt;jspm install jquery&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Import the library with a single line of code, no need to external reference inside your HTML file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;display.js&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var $ = require(&apos;jquery&apos;);

$(&apos;body&apos;).append(&quot;I&apos;ve imported jQuery!&quot;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
&lt;p&gt;Then you configure these things within &lt;code class=&quot;language-text&quot;&gt;System.config({ ... })&lt;/code&gt; before
importing your module. Normally when run &lt;code class=&quot;language-text&quot;&gt;jspm init&lt;/code&gt;, there will be a file
named &lt;code class=&quot;language-text&quot;&gt;config.js&lt;/code&gt; for this purpose.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To make these scripts run, we need to load &lt;code class=&quot;language-text&quot;&gt;system.js&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;config.js&lt;/code&gt; on the HTML page. After that, we will load the &lt;code class=&quot;language-text&quot;&gt;display.js&lt;/code&gt; file using
the &lt;code class=&quot;language-text&quot;&gt;SystemJS&lt;/code&gt; module loader.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;script src=&quot;jspm_packages/system.js&quot;&gt;&amp;lt;/script&gt;
&amp;lt;script src=&quot;config.js&quot;&gt;&amp;lt;/script&gt;
&amp;lt;script&gt;
  System.import(&quot;scripts/display.js&quot;);
&amp;lt;/script&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Noted: You can also use &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;Webpack&lt;/code&gt; as Angular 2 has applied it. Since &lt;code class=&quot;language-text&quot;&gt;jspm&lt;/code&gt; was developed to integrate with &lt;code class=&quot;language-text&quot;&gt;SystemJS&lt;/code&gt; and it works on top of the existing &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; source, so your answer is up to you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-task-runner&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-task-runner&quot; aria-label=&quot;3 task runner permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Task runner&lt;/h2&gt;
&lt;p&gt;Task runners and build tools are primarily command-line tools. Why we need to use them: In one word: &lt;strong&gt;automation&lt;/strong&gt;. The less work you have to do when performing repetitive tasks like &lt;strong&gt;minification, compilation, unit testing, linting&lt;/strong&gt; which previously cost us a lot of times to do with command line or even manually.&lt;/p&gt;
&lt;h3 id=&quot;grunt&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#grunt&quot; aria-label=&quot;grunt permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can create automation for your development environment to pre-process codes or create build scripts with a config file and it seems very difficult to handle a complex task. Popular in the last few years.&lt;/p&gt;
&lt;p&gt;Every task in &lt;code class=&quot;language-text&quot;&gt;Grunt&lt;/code&gt; is an array of different plugin configurations, that simply get executed one after another, in a strictly independent, and sequential fashion.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;clean&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;build/app.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build/vendor.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token literal-property property&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build/app.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build/dist/app.js&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token literal-property property&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&apos;build/app.js&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;build/vendors.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build/app.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// ... other task configurations ...&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;clean&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bower&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;browserify&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;concat&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;copy&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;gulp&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gulp&quot; aria-label=&quot;gulp permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://gulpjs.com/&quot;&gt;Gulp&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Automation just like &lt;code class=&quot;language-text&quot;&gt;Grunt&lt;/code&gt; but instead of configurations, you can write &lt;code class=&quot;language-text&quot;&gt;JavaScript&lt;/code&gt; with streams like it’s a node application. Prefer these days.&lt;/p&gt;
&lt;p&gt;This is a &lt;code class=&quot;language-text&quot;&gt;Gulp&lt;/code&gt; sample task declaration.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//import the necessary gulp plugins&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; gulp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gulp&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sass &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gulp-sass&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; minifyCss &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gulp-minify-css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; rename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gulp-rename&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//declare the task&lt;/span&gt;
gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sass&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gulp
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./scss/ionic.app.scss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./www/css/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;minifyCss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;keepSpecialComments&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;extname&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;.min.css&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./www/css/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;end&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; done&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;See more: &lt;a href=&quot;https://medium.com/@preslavrachev/gulp-vs-grunt-why-one-why-the-other-f5d3b398edc4#.fte0nahri&quot;&gt;https://medium.com/@preslavrachev/gulp-vs-grunt-why-one-why-the-other-f5d3b398edc4#.fte0nahri&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-scaffolding-tools&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-scaffolding-tools&quot; aria-label=&quot;4 scaffolding tools permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Scaffolding tools&lt;/h2&gt;
&lt;h3 id=&quot;slush-and-yeoman&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slush-and-yeoman&quot; aria-label=&quot;slush and yeoman permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Slush and Yeoman&lt;/h3&gt;
&lt;p&gt;You can create starter projects with them. For example, you are planning to build a prototype with HTML and SCSS, then instead of manually create some folder like scss, css, img, fonts. You can just install &lt;code class=&quot;language-text&quot;&gt;yeoman&lt;/code&gt; and run a simple script. Then everything here for you.&lt;/p&gt;
&lt;p&gt;Find more &lt;a href=&quot;http://yeoman.io/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm install -g yo
npm install --global generator-h5bp
yo h5bp&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;See more: &lt;a href=&quot;https://www.quora.com/What-are-the-differences-between-NPM-Bower-Grunt-Gulp-Webpack-Browserify-Slush-Yeoman-and-Express&quot;&gt;https://www.quora.com/What-are-the-differences-between-NPM-Bower-Grunt-Gulp-Webpack-Browserify-Slush-Yeoman-and-Express&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;My answer is not matched with the content of the question but when I’m searching for this knowledge on Google, I always see the question on top so that I decided to answer it in summary. I hope you guys found it helpful.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Migrating my blog from Github to Gitlab, and deploy to Netlify]]></title><description><![CDATA[Still use Github for most of the work, but Gitlab for my blog]]></description><link>https://trungvose.commoving-away-from-github/</link><guid isPermaLink="false">https://trungvose.commoving-away-from-github/</guid><pubDate>Mon, 08 Jun 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yesterday morning, I logged in to Github and it came to surprise me with a message:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your account has been flagged.
Because of that, your profile is hidden from the public. If you believe this is a mistake, contact support to have your account status reviewed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I contacted the support team immediately to tell them that was a mistake by their bot. I google about this issue. It is not uncommon and happening all the time.&lt;/p&gt;
&lt;p&gt;Usually, I don’t care about Github. But because this blog is hosted on Github page and my account was flagged, this blog was not accessible at all.&lt;/p&gt;
&lt;p&gt;This morning, they still haven’t removed the flag. I check the analytics and see the number of visitors dropped like crazy.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 730px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8797e5b31387b89c3a7449f02a471f25/e9beb/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.16666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACqklEQVQ4y32Ty08TQRzH95/wYjx4MESNwaj44KIiiQhqPBiNF0ADAQQMLcbySEwMifGAjxgFQ+AgHEy86MH46EFvnjzhtqUtFCggIS2Fsu3uzs5jv2ZmYUsUneSXX2Z25zO/11dLLRnQZzLQZ7LI5CkYpSiaJorFIgQXcBwHpmmCCwHXdXc0IQSI48AmFBrA4BkFINRHxhgoZRDCBefeXl6Uaztoa885B3EobOJA0xcYhsMWvurU+wHbl+uf/Cs6CZOP+8CxT6vYdVVHbf8sOkeyCIxm8fRDAQ/eGkgscxAO2FQR/4LJtZWRBErTJsJZHGjUcSGUQHlTBCdux1B5J4599RE0PF7GxYEVTHwz1GXKxI71k0AZnYpwPJxFWb0EJlHZMYWzwTjOdcdxtCWKup4E9t6YxND7Fb8IXLiqthIkG2bbtvJ0E6qNf/GANaEkTrbHcLorjqpgHIebo7jUN43ypigCw0sY+ZzHu+9GqbquC8uyYFm2nz6l9P/Aut4kjrXGcP5eEruv/cSV+ylkNgSWchyUu1hfW4NhFHyYfEDVcH+jjtqeUsrVdxM40uJFWNEWw+X+aRxvi6pHa/pmUR1KIZPnMI1V5HI5FeHWvGpjHzPYc30SZwJxlZ5syqmOKRV1VXccB29GlC9r0NV5RWsUh25F8OjNCtK/8sis5hWIEKIEoYV/5FH/MIXQyCLan80j8DKN4NACmgfn0De6iJYnc+gdXUTz4CyCQ2l0vUij83kaoVezGHg9j8icJVsFyyZeU4TrghAbjDoQgqsRkJ4zCtcVoA5RM8gY9YbcFf7AE0cqCJvSo5705KFpWqr9jHsfGOOqyJRxVRulBsZKUhOeCbc0Qp6WHWgSZBiGar+8KCP0jP/hma/xjaKDfIFgvUBQMB1fdlIpvwF95dIyeaBjCgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrating my blog to Gitlab and deploy to Netlify&quot;
        title=&quot;Migrating my blog to Gitlab and deploy to Netlify&quot;
        src=&quot;/static/8797e5b31387b89c3a7449f02a471f25/e9beb/01.png&quot;
        srcset=&quot;/static/8797e5b31387b89c3a7449f02a471f25/8ff5a/01.png 240w,
/static/8797e5b31387b89c3a7449f02a471f25/e85cb/01.png 480w,
/static/8797e5b31387b89c3a7449f02a471f25/e9beb/01.png 730w&quot;
        sizes=&quot;(max-width: 730px) 100vw, 730px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 806px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1cd62d75ed0a318d414b57a7ae60f9e8/764be/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAACGUlEQVQ4y61Sy27aUBT0n1RJSXmUVxwghoaQVHlt2qpSFak8bANV1U9ky4oFv8A7BgzYYLBZT3Uu3MoYaLroYnS4lzlzZnyu8DaVgy8SQzwehCiGceIP4l04jNNAAAlJQjyVhC/gRyQWgu99GNFEAh9yN4heXODU78ebszOc+APwhULwBYMQkpkcLqUrpKU0xHMRsZiIcCSOZFJCJnONSPQcoWAID/dZPN3e4SmdRerxCzLZjxCvHpCT7vApG8fNbQrJ1CWEspJHWS1BURQoClUZslxAtVpGUa4gn8+johZRLBbw84cKtVRBXq7g2/N3fP76jFKhiF/VEhS1jJKsQphOp5jP5zBNcwcGwTCwWCywXq9h2w4WlgXDMDHf4Rlb7uYskNhqtcJyuWSVg862bYMG1mo11Ot1tFotJu7luiGQKhEsy9oDCU4mEzQaDTSbTfR6PTiOc5DLsSPoFibnPDKJ0HSLRd7ccZ67h37vOaRG23GY4HA4hD7RWdU0jdXRaITZbPbns7hFdwRpKsXr9/vodDrodrsYDjX0NR3twRjdFx3t/gjtgY4XbYTBYMCG6LrOhnPRvci0BH08ZkRyQqA7qjSQKtuuMWPx/ykyi73dMMU055snwR3xLbuNcBxdCj9To7l9k7zJyznq0PsnryS2E48JW3vuDgp64b73Djpk5K+Cx1x7ue7zqw5fA++jZ0ef5b8JUiXR32A/jabToagTAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrating my blog to Gitlab and deploy to Netlify&quot;
        title=&quot;Migrating my blog to Gitlab and deploy to Netlify&quot;
        src=&quot;/static/1cd62d75ed0a318d414b57a7ae60f9e8/764be/02.png&quot;
        srcset=&quot;/static/1cd62d75ed0a318d414b57a7ae60f9e8/8ff5a/02.png 240w,
/static/1cd62d75ed0a318d414b57a7ae60f9e8/e85cb/02.png 480w,
/static/1cd62d75ed0a318d414b57a7ae60f9e8/764be/02.png 806w&quot;
        sizes=&quot;(max-width: 806px) 100vw, 806px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And I have submitted to be a part of &lt;a href=&quot;https://www.freecodecamp.org/forum/t/how-i-can-write-a-blog-for-freecodecamp/328739&quot;&gt;Freecodecamp&lt;/a&gt; writer. I am looking forward to that opportunity to share my knowledge with people and get feedback, and learn from the feedback. Probably, they will review some of my blog posts this week. And think about if I don’t get selected because of a mistake by Github bot that leads my blog to become inaccessible, no chance. That’s why I need Github to resolve it asap. But in this situation of the corona virus, it won’t happen in a blink of the eyes I guess.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 825px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/706f1114c743eb6a8909f619e4517eff/d4c13/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABWklEQVQoz41Ri26DMBDj//9xox2E8c6Td8CTg0CtVLpGsny63Dm+SwQAzlrEcYw0FTCqRCYSlFWFYRhgrYVzDl3XwRqLvu9hnYNz3Zlf15UyGMcR0bIsoahpGrSthFIyFPLS+wVXx3sfhNh/gLlonmcYY1AUJaSUaNs2sFIqCPMxgk4OMM8eQmkdmJMEh3xtmiZorU9QkExxFvOexWSugUwj5Mc4OKQgrTPJ4gO85BjHfj490bZtIWBzJlIYo9H33VnAW9Z8iidBkTcoao2q3cd+5+7ou3Q4Lx6xaBELiZtoIJWBsQ6uGwJb16Hrh/Ap3CcenF8K3kWDNJdIflvcMolb2uCHsWjw9VOjqBWs0Zim8cHldrVDjyRXEIVBSi53Tgu9x4VCVhokuUYpR/h1ezn6k8PvpMKdY6c17pncWdDhnqPTopLoxxl725uR13WDtPO/8H49x3z1MX86HaV1yEh6pwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migrating my blog to Gitlab and deploy to Netlify&quot;
        title=&quot;Migrating my blog to Gitlab and deploy to Netlify&quot;
        src=&quot;/static/706f1114c743eb6a8909f619e4517eff/d4c13/03.png&quot;
        srcset=&quot;/static/706f1114c743eb6a8909f619e4517eff/8ff5a/03.png 240w,
/static/706f1114c743eb6a8909f619e4517eff/e85cb/03.png 480w,
/static/706f1114c743eb6a8909f619e4517eff/d4c13/03.png 825w&quot;
        sizes=&quot;(max-width: 825px) 100vw, 825px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Having said that, all of &lt;u&gt;my repositories&lt;/u&gt; on github are &lt;u&gt;currently inaccessible&lt;/u&gt;. If you need a specific source code, please send me a message on &lt;code class=&quot;language-text&quot;&gt;trungk18 [et] gmail [dot] com&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So I decided to migrate all of the content to &lt;a href=&quot;https://gitlab.com/&quot;&gt;Gitlab&lt;/a&gt; and &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; this morning. Which took me about 15 minutes, including configuring the DNS.&lt;/p&gt;
&lt;p&gt;If you are reading this post, meaning you are seeing my blog hosted on Netlify, not a GitHub page anymore.&lt;/p&gt;
&lt;p&gt;If you like to do the same thing, and I recommend you to do so. You could start with a well-written guide from Netlify.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.netlify.com/blog/2020/04/02/a-step-by-step-guide-jekyll-4.0-on-netlify/&quot;&gt;A Step-by-Step Guide: Jekyll 4.0 on Netlify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.netlify.com/domains-https/custom-domains/#definitions&quot;&gt;Netlify Custom domains&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And welcome again :)&lt;/p&gt;
&lt;p&gt;Thanks for reading and visiting my blog.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What is JavaScript Closure?]]></title><description><![CDATA[In short, a closure is not the function that is returned in another function. A closure is a combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function]]></description><link>https://trungvose.comjavascript-closure/</link><guid isPermaLink="false">https://trungvose.comjavascript-closure/</guid><pubDate>Fri, 29 May 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I still remember an interview with the customer five years ago when I worked in Hanoi. I was just graduated from university at that time and didn’t have much experience.&lt;/p&gt;
&lt;p&gt;There were some basic questions such as &lt;code class=&quot;language-text&quot;&gt;&quot;1&quot; + 1 will evaluate in what value?&lt;/code&gt;. I knew the answer is &lt;code class=&quot;language-text&quot;&gt;&quot;11&quot;&lt;/code&gt; but have no idea what is &lt;a href=&quot;https://medium.freecodecamp.org/js-type-coercion-explained-27ba3d9a2839&quot;&gt;type coercion&lt;/a&gt; and why the value is &lt;code class=&quot;language-text&quot;&gt;&quot;11&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And then the guys started asking about &lt;strong&gt;Closure&lt;/strong&gt;. I answered in a very naive way &lt;code class=&quot;language-text&quot;&gt;It is a function which is return in another function...&lt;/code&gt;. The guys on the phone weren’t laughing but I know that I was wrong. I only read about Closure from &lt;a href=&quot;https://www.w3schools.com/js/js_function_closures.asp&quot;&gt;w3schools&lt;/a&gt; before.&lt;/p&gt;
&lt;p&gt;Closure is not easy to digest. It has always been a bit of a mystery to me. I have read multiple articles, I have used closures in my work, sometimes I even used a closure without realizing I was using a closure. After more than four years working with JavaScript and has built many applications from small to large scale that serve ten of thousands of users, I hope that I can properly answer this question.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many parts of this blog post are heavily influenced by and taken from an excellent article on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures&quot;&gt;MDN&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;execution-context&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#execution-context&quot; aria-label=&quot;execution context permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Execution context&lt;/h2&gt;
&lt;p&gt;Some concepts are important to understand before you can fully get the idea of closures. One of them is the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this&quot;&gt;execution context&lt;/a&gt;. &lt;a href=&quot;http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/&quot;&gt;This article&lt;/a&gt; has a very good primer on Execution Context.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 554px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d00f8476b42596dceeb4b39f79068c97/577b7/02.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAQABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdqpoQg//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGxAAAgEFAAAAAAAAAAAAAAAAAAEREDFRkfD/2gAIAQEAAT8htg0Mjooz/9oADAMBAAIAAwAAABDgz//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EFf/xAAWEQEBAQAAAAAAAAAAAAAAAAAAESH/2gAIAQIBAT8QxX//xAAfEAABAwMFAAAAAAAAAAAAAAABABExIUFRYXGBofH/2gAIAQEAAT8QDSs0TjPNGxFAaWTC0dkwPimNsL//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;What is JavaScript Closure?&quot;
        title=&quot;What is JavaScript Closure?&quot;
        src=&quot;/static/d00f8476b42596dceeb4b39f79068c97/577b7/02.jpg&quot;
        srcset=&quot;/static/d00f8476b42596dceeb4b39f79068c97/09b79/02.jpg 240w,
/static/d00f8476b42596dceeb4b39f79068c97/7cc5e/02.jpg 480w,
/static/d00f8476b42596dceeb4b39f79068c97/577b7/02.jpg 554w&quot;
        sizes=&quot;(max-width: 554px) 100vw, 554px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-closure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-a-closure&quot; aria-label=&quot;what is a closure permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is a Closure?&lt;/h2&gt;
&lt;p&gt;Consider the following example&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//sum: outer function&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//add: inner function&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sumTwo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sumOfTwoAndThree &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sumTwo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Function &lt;code class=&quot;language-text&quot;&gt;sum&lt;/code&gt; was created with an argument &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt;. When invoke &lt;code class=&quot;language-text&quot;&gt;sum&lt;/code&gt;, it will return another function &lt;code class=&quot;language-text&quot;&gt;add&lt;/code&gt; that has &lt;code class=&quot;language-text&quot;&gt;b&lt;/code&gt; as the local argument. Noted that how &lt;code class=&quot;language-text&quot;&gt;add&lt;/code&gt; also has access to &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt;, which is the argument of the outer function &lt;code class=&quot;language-text&quot;&gt;sum&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I created a variable &lt;code class=&quot;language-text&quot;&gt;sumTwo&lt;/code&gt; and assigned it to the return value of &lt;code class=&quot;language-text&quot;&gt;sum(2)&lt;/code&gt;. At this point, &lt;code class=&quot;language-text&quot;&gt;sumTwo&lt;/code&gt; is actually another function that can be invoked, which is &lt;code class=&quot;language-text&quot;&gt;add(b)&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I created another variable &lt;code class=&quot;language-text&quot;&gt;sumOfTwoAndThree&lt;/code&gt; and assigned it to the return value of &lt;code class=&quot;language-text&quot;&gt;sumTwo(3)&lt;/code&gt;, which is 5.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It means that somehow, the &lt;code class=&quot;language-text&quot;&gt;sumTwo&lt;/code&gt; function still has access to &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;, and then do the add operation with &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; that I passed in.&lt;/p&gt;
&lt;p&gt;Where is the value &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; of an argument &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; stored, even the function &lt;code class=&quot;language-text&quot;&gt;sum&lt;/code&gt; has already been executed and its value was assigned to &lt;code class=&quot;language-text&quot;&gt;sumTwo&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;In some programming languages, the local variables within a function exist for just the duration of that function’s execution. Once &lt;code class=&quot;language-text&quot;&gt;sum()&lt;/code&gt; finishes executing, you might expect that the &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; argument would no longer be accessible. However, because the code still works as expected, this is obviously not the case in JavaScript.&lt;/p&gt;
&lt;p&gt;The reason is that functions in JavaScript form &lt;strong&gt;closures&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;u&gt;closure&lt;/u&gt; is a combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives &lt;strong&gt;you access to an outer function’s scope from an inner function&lt;/strong&gt;. &lt;u&gt;Even when the outer function has already been invoked and returned&lt;/u&gt;.&lt;/p&gt;
&lt;p&gt;In JavaScript, closures are created every time a function is created, at function creation time.&lt;/p&gt;
&lt;p&gt;Whenever you &lt;u&gt;declare a new function&lt;/u&gt; and assign it to a variable, you store the function definition, as well as a &lt;u&gt;closure&lt;/u&gt;. The closure contains all the variables that are in scope at the time of creation of the function. It is analogous to a backpack. A function definition comes with a little backpack. And in its pack it stores all the variables that were in scope at the time that the function definition was created.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 570px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/305145d7fdd6d63bf7d3b62d26f3c63c/432e7/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACA0lEQVQoz0WSS4/TMBSF8wuQgAXMNInt2M47cZw2TadNO50HEhs2rJCAPRL/f/2heAZYfPKVbB3fc8+N2q6jHTr8MNL3jnk+MPqRRBl65xkGhzYaYwzW5hhrw5nnRaAoSpTKeH565np/JTqfz1yuF5q6DZd13VAWBbEyVP3ItPOUtqKpWsqipikcZVkhhEQphZQrEmsseZ4THY8L03bCmJeftTahFlIilSIzmrLvKNqGbqjoXEVea6QSJElCmr6QJHEgcs2Abzxd39O27T/hdO1AClKpUU2PqmpaV+K8JbOCNJNs4oTNJg3E8QvRyY188iPWvHRmXzGZCLNRKuZu23KePP3O0c8Th2Xg8WFiP3ZUZUpRxBRFEogu2z0Puz3a5Ghbom2BsSWuEmE+dS35/NTzvLTcLwX314rlpDkeEk5HweGwYZ7/E81ff7P78os3x2+8v/zg3fk7by8/uTUNSqToTJApzYePG25vE25uYm5uEm43q90k2PxreyVqh5lhWHD+QOdnGj/R+gkZ7CqEUqgso6kMmRLkVlLmEpGugnEghPMaUOS7gWU4su8mxt7j+h7XdcGuMRqhC/J+x3y5MBwWtsvC7nSHtRbnarZjQVlmSC1J05Ro72cedo+4zrGdJrwf2e6mEMi6zOujNfFUZshMI5QOtRCCLFNoLZHrNog0CP4BdLgnJ6qXZlYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;What is JavaScript Closure?&quot;
        title=&quot;What is JavaScript Closure?&quot;
        src=&quot;/static/305145d7fdd6d63bf7d3b62d26f3c63c/432e7/01.png&quot;
        srcset=&quot;/static/305145d7fdd6d63bf7d3b62d26f3c63c/8ff5a/01.png 240w,
/static/305145d7fdd6d63bf7d3b62d26f3c63c/e85cb/01.png 480w,
/static/305145d7fdd6d63bf7d3b62d26f3c63c/432e7/01.png 570w&quot;
        sizes=&quot;(max-width: 570px) 100vw, 570px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Look at the above screenshot from VSCode debugger, you will see that when invoked the &lt;code class=&quot;language-text&quot;&gt;sumTwo(3)&lt;/code&gt;, the closure contains &lt;code class=&quot;language-text&quot;&gt;a : 2&lt;/code&gt;. While the local scope contains &lt;code class=&quot;language-text&quot;&gt;b : 3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You could also invoke the function differently.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//sum: outer function&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//add: inner function&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Is equivalent to...&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Which becomes...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;usage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#usage&quot; aria-label=&quot;usage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Usage&lt;/h2&gt;
&lt;h3 id=&quot;emulating-private-methods-with-closures&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#emulating-private-methods-with-closures&quot; aria-label=&quot;emulating private methods with closures permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Emulating private methods with closures&lt;/h3&gt;
&lt;p&gt;I usually rely on &lt;a href=&quot;https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript&quot;&gt;Revealing Module Pattern&lt;/a&gt; to have the ability to define private property and method. One of the examples I have already published, you can read &lt;a href=&quot;/blog/limit-the-number-of-simultaneous-ajax-requests/&quot;&gt;Limit the number of simultaneous ajax requests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I only exposed the &lt;code class=&quot;language-text&quot;&gt;addRequest&lt;/code&gt; to the outside world. The rest will stay inside the closure and nobody else can access it. By doing so, I have control over my code, and also don’t pollute the global context by introducing global variables.&lt;/p&gt;
&lt;h3 id=&quot;event-handler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#event-handler&quot; aria-label=&quot;event handler permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Event handler&lt;/h3&gt;
&lt;p&gt;Much of the code written in front-end JavaScript is &lt;strong&gt;event-based&lt;/strong&gt;. You define some behavior, and then attach it to an event that is triggered by the user (such as a click or a keypress). The code is attached as a callback (a single function that is executed in response to the event).&lt;/p&gt;
&lt;p&gt;For instance, suppose we want to add buttons to a page to adjust the text size. One way of doing this is to specify the font-size of the body element (in pixels), and then set the size of the other elements on the page (such as headers) using the relative em unit:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Helvetica&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Arial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 12px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;h1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.5em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;h2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Such interactive text size buttons can change the font-size property of the body element, and the adjustments are picked up by other elements on the page thanks to the relative units.&lt;/p&gt;
&lt;p&gt;Here’s the JavaScript:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeSizer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fontSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;px&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; size12 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeSizer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; size14 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeSizer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; size16 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeSizer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;size12&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;size14&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;size16&lt;/code&gt; are now functions that resize the body text to 12, 14, and 16 pixels, respectively. You can attach them to buttons (in this case hyperlinks) as demonstrated in the following code example.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;size-12&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onclick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; size12
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;size-14&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onclick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; size14
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;size-16&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onclick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; size16&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;size-12&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;12&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;size-14&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;14&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;size-16&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;16&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;300&quot; src=&quot;//jsfiddle.net/vnkuZ/7726/embedded/&quot; allowfullscreen=&quot;allowfullscreen&quot; allowpaymentrequest frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;h3 id=&quot;partial-application-and-currying&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#partial-application-and-currying&quot; aria-label=&quot;partial application and currying permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Partial application and currying&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://stackoverflow.com/questions/218025/what-is-the-difference-between-currying-and-partial-application&quot;&gt;this answer&lt;/a&gt; for more detail.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/111111/3375906&quot;&gt;https://stackoverflow.com/a/111111/3375906&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/35320692/3375906&quot;&gt;https://stackoverflow.com/a/35320692/3375906&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8&quot;&gt;https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/whats-a-javascript-closure-in-plain-english-please-6a1fc1d2ff1c/&quot;&gt;https://www.freecodecamp.org/news/whats-a-javascript-closure-in-plain-english-please-6a1fc1d2ff1c&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Angular - Using Visitor design pattern with Typescript]]></title><description><![CDATA[If you have multiple concrete classes that inherit from the same base class, or implement the same interface. You should consider using visitor pattern. It will save you from dozens of if-else block or switch/case and typecasting]]></description><link>https://trungvose.comtypescript-visitor-pattern-with-google-maps-api/</link><guid isPermaLink="false">https://trungvose.comtypescript-visitor-pattern-with-google-maps-api/</guid><pubDate>Sun, 24 May 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - If you have multiple concrete classes that inherit from the same base class, or implement the same interface. You should consider using visitor pattern. It will save you from dozens of if-else block or switch/case and typecasting.&lt;/p&gt;
&lt;h2 id=&quot;update&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update&quot; aria-label=&quot;update permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update&lt;/h2&gt;
&lt;p&gt;I presented it at &lt;a href=&quot;https://github.com/SingaporeJS/talk.js/issues/39&quot;&gt;SingaporeJS - talk.js - July 2020&lt;/a&gt;. See the slide deck:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://slides.com/trungvose/angular-using-visitor-design-pattern-with-typescript&quot;&gt;https://slides.com/trungvose/angular-using-visitor-design-pattern-with-typescript&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-visitor-pattern&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-visitor-pattern&quot; aria-label=&quot;what is visitor pattern permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is visitor pattern&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Visitor is a behavioral design pattern that allows adding new behaviors to existing class hierarchy without altering any existing code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There weren’t a lot of examples on Visitor pattern because of its popularity compared to the well-known Factory or Command pattern. And with the available example that I could find, it is &lt;a href=&quot;https://refactoring.guru/design-patterns/visitor/typescript/example&quot;&gt;very conceptual&lt;/a&gt;, you wouldn’t be able to imagine how to use it in your real-world use case.&lt;/p&gt;
&lt;p&gt;At Zyllem, we are using it extensively on the server-side code. On the client-side, I took me sometimes to have my first visitor running on production. The example below was not that first one though 😁&lt;/p&gt;
&lt;h2 id=&quot;use-case&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#use-case&quot; aria-label=&quot;use case permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Use case&lt;/h2&gt;
&lt;p&gt;In my application, I have a map view that displaying &lt;u&gt;a route&lt;/u&gt; which contains:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A few places on sequence, such as 1 -&gt; 2 &gt; 3 -&gt; 4. Let call it a Point.&lt;/li&gt;
&lt;li&gt;A current driver’s location. Let call it a real-time/live location.&lt;/li&gt;
&lt;li&gt;A location where the route is started (noted that it isn’t the first point that I mention above). Let call it a Start location.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So easily you could see there are three types of markers that I need to display on the maps. Each of them will have the behaviors:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Different icon on the maps.&lt;/li&gt;
&lt;li&gt;Show a different message when hovering over.&lt;/li&gt;
&lt;li&gt;Upon click/ mouseover/ mouse out events, do something.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See the gif below from my actual application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/69430292674b190e40d4b479d711f9dc/01.gif&quot; alt=&quot;Using Visitor design pattern with Typescript&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;I will walk through how I built it with visitor design pattern. Noted that it is the much simpler version than the actual one. I have removed all the complicated icons with custom HTML real-time communication for the live location and so on. You can view the running example at the end of this post on &lt;a href=&quot;https://stackblitz.com/edit/angular-typescript-visitor-pattern-with-google-maps-api&quot;&gt;stackblitz&lt;/a&gt;. Or view the completed &lt;a href=&quot;https://github.com/trungvose/angular-typescript-visitor-design-pattern-with-google-maps-api&quot;&gt;source code at github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;I am using &lt;a href=&quot;https://developers.google.com/maps/documentation/javascript/tutorial&quot;&gt;Google Maps&lt;/a&gt; (GM) to display all of these data on a map view. For GM, to display a marker/pin on maps, we could use &lt;a href=&quot;https://developers.google.com/maps/documentation/javascript/reference/marker#Marker&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;google.maps.Marker&lt;/code&gt;&lt;/a&gt;. To display a route, It has &lt;a href=&quot;https://developers.google.com/maps/documentation/javascript/reference/polygon#Polyline&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;google.maps.Polyline&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;GM will consider all the marker as &lt;code class=&quot;language-text&quot;&gt;google.maps.Marker&lt;/code&gt;. But from my point of view I will have one base class the representing a pin on GM. And three concrete classes that corresponded to three types of markers I listed above. Think about a base class name &lt;code class=&quot;language-text&quot;&gt;CustomMarker&lt;/code&gt;. And three concrete classes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PointMarker&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;RealTimeLocationMarker&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;StartLocationMarker&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;api-data-model&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-data-model&quot; aria-label=&quot;api data model permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API Data Model&lt;/h3&gt;
&lt;p&gt;A mentioned above, I have a &lt;code class=&quot;language-text&quot;&gt;RouteModel&lt;/code&gt; which include a list of &lt;code class=&quot;language-text&quot;&gt;PointModel&lt;/code&gt;, a current live location &lt;code class=&quot;language-text&quot;&gt;RealTimeLocationModel&lt;/code&gt; and a start location &lt;code class=&quot;language-text&quot;&gt;StartLocationModel&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RouteModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PointModel&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;startLocation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StartLocationModel&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;realTimeLocation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RealtimeLocationModel&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;//And many more properties&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;pathFromStartLocation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; any&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PointModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LocationApi&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RealtimeLocationModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;geoCoordinate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GeoCoordinateApi&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;capturedTimeStamp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StartLocationModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LocationApi&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;marker-data-model&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#marker-data-model&quot; aria-label=&quot;marker data model permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Marker Data Model&lt;/h3&gt;
&lt;p&gt;I use generic to pass the type &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; into a property named &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; to hold an actual object that I have received from the API. The rest are all the necessary info that GM required to display a marker such as the &lt;code class=&quot;language-text&quot;&gt;google.maps.LatLng&lt;/code&gt;. We need the &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; property to know which marker is this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And most importantly for the visitor pattern to work, you have to define an abstract method that takes the base &lt;code class=&quot;language-text&quot;&gt;CustomMarkerVisitor&lt;/code&gt; interface as an argument.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomMarker&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarkerType
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; position&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LatLng
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; popupContent&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/**
   * The CustomMarker declares an `accept` method that should take the base
   * visitor interface as an argument.
   */&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;visitor&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarkerVisitor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; CustomMarkerType &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;POINT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;POINT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;START_LOCATION&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;START_LOCATION&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;REAL_TIME_LOCATION&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;REAL_TIME_LOCATION&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For each concrete class, you will have to implement the &lt;code class=&quot;language-text&quot;&gt;accept&lt;/code&gt; method, because it is defined as an abstract method.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PointMarker&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomMarker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PointModel&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//...code removed for brevity&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PointModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;point&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;visitor&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarkerVisitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    visitor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;visitPointMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StartLocationMarker&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomMarker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;StartLocationModel&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//...code removed for brevity&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;startLocation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StartLocationModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startLocation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;visitor&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarkerVisitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    visitor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;visitStartLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RealTimeLocationMarker&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomMarker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;RealtimeLocationModel&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//...code removed for brevity&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;realTimeLocation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RealtimeLocationModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;realTimeLocation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;visitor&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarkerVisitor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    visitor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;visitRealTimeLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * Concrete class may have special methods that don&apos;t exist in their
   * base class or interface. The Visitor is still able to use these methods
   * since it&apos;s aware of the component&apos;s concrete class.
   */&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;concreteMethodOfRealTimeLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Real time&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;visitor-interface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#visitor-interface&quot; aria-label=&quot;visitor interface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Visitor interface&lt;/h3&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;CustomMarkerVisitor&lt;/code&gt; interface declares a set of visiting methods that correspond to the number of concrete &lt;code class=&quot;language-text&quot;&gt;CustomMarker&lt;/code&gt; classes. The signature of a visiting method allows the visitor to identify the exact class of the component that it’s dealing with.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomMarkerVisitor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;visitPointMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;markerData&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PointMarker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;visitStartLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;markerData&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StartLocationMarker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;visitRealTimeLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;markerData&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RealTimeLocationMarker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A concrete CustomMarkerVisitor implement several versions of the same algorithm, which can work with all concrete &lt;code class=&quot;language-text&quot;&gt;CustomMarker&lt;/code&gt; classes. Think about it as a behavior that each concrete &lt;code class=&quot;language-text&quot;&gt;CustomMarker&lt;/code&gt; need to have such as click, double click, mouse over, mouse out. For each behavior, we will have a corresponding visitor to handle.&lt;/p&gt;
&lt;p&gt;For example, I have a specific visitor &lt;code class=&quot;language-text&quot;&gt;MarkerMouseClickVisitor&lt;/code&gt; to handle the marker click event.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkerMouseClickVisitor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomMarkerVisitor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MapApiService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;visitPointMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PointMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;visitStartLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StartLocationMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;visitRealTimeLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RealTimeLocationMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//You could call this method too&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//marker.concreteMethodOfRealTimeLocation();&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarker&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; clicked&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;comparison-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparison-code&quot; aria-label=&quot;comparison code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparison Code&lt;/h3&gt;
&lt;p&gt;I used to do it differently with all the switch/case block. See the example code below for the same UI behavior with switch/case approach and visitor pattern with a mouseover behavior. I personally like the visitor better. Because I used to have the switch/case for every single behavior and I don’t know, I just don’t like too many switch/case blocks. Noted that the below implementation was a much simpler version on my real-world application where it involves router and other services as well. Separated it into a visitor helped me to better understand and isolate my code if there is any bug.&lt;/p&gt;
&lt;h4 id=&quot;switchcase&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#switchcase&quot; aria-label=&quot;switchcase permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Switch/Case&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;addMarkerToMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;markerData&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarker&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; marker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Marker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mouseover&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openInfoWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;popupContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; CustomMarkerType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;POINT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; mouse over&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; CustomMarkerType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;START_LOCATION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; mouse over&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; CustomMarkerType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;REAL_TIME_LOCATION&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; mouse over&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;visitor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#visitor&quot; aria-label=&quot;visitor permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Visitor&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;addMarkerToMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;markerData&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarker&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; marker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Marker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mouseover&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openInfoWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;popupContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    markerData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkerMouseOverVisitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;markers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MarkerMouseOverVisitor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomMarkerVisitor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MapApiService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;visitPointMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PointMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;visitStartLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StartLocationMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;visitRealTimeLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RealTimeLocationMarker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;marker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomMarker&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;marker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; mouse over&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You see how we still can access to &lt;code class=&quot;language-text&quot;&gt;markerData&lt;/code&gt; variable on the callback of the mouseover despite the &lt;code class=&quot;language-text&quot;&gt;addMarkerToMap&lt;/code&gt; has been finished executing. If you have been working with JavaScript long enough, you will know what I am trying to say.&lt;/p&gt;
&lt;p&gt;It is &lt;u&gt;JavaScript Closure&lt;/u&gt;.&lt;/p&gt;
&lt;h2 id=&quot;pros-and-cons&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pros-and-cons&quot; aria-label=&quot;pros and cons permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Pros and cons&lt;/h2&gt;
&lt;p&gt;The benefits of visitor pattern, I think:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reduce the number of doing switch/case for each behavior, see my code comparison table below. You will understand.&lt;/li&gt;
&lt;li&gt;If I introduce a new type of &lt;code class=&quot;language-text&quot;&gt;CustomMarker&lt;/code&gt;, what I need to do is to update the &lt;code class=&quot;language-text&quot;&gt;CustomMarkerVisitor&lt;/code&gt; with a new method. The compiler won’t build until I come to every implementation of &lt;code class=&quot;language-text&quot;&gt;CustomMarkerVisitor&lt;/code&gt; to implement it properly with the new method. So that I will not afraid of missing behavior.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But also, the visitor pattern has some downside as I realize:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Not so easy for a junior developer to pick it up.&lt;/li&gt;
&lt;li&gt;Too many boilerplate, even if you don’t want your concrete class to have a specific behavior. You are forced to implement it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But on this use case of the map, I like how visitor pattern has transformed my code.&lt;/p&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-typescript-visitor-pattern-with-google-maps-api?embed=1&amp;file=src/app/model/visitor/marker-mouse-click-visitor.ts&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;img src=&quot;/f019f620a4b7b17be58dd230e41ccf42/02.gif&quot; alt=&quot;Using Visitor design pattern with Typescript&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/angular-typescript-visitor-design-pattern-with-google-maps-api&quot;&gt;https://github.com/trungvose/angular-typescript-visitor-design-pattern-with-google-maps-api&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I hope it will help you guys get the idea of visitor pattern :) I know I am not a good writer yet, appreciate all your comments and contributions.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Front end editorial style guide]]></title><description><![CDATA[A style guide on the standard format, spelling, and construction of commonly used words and phrases]]></description><link>https://trungvose.comfront-end-editorial-style-guide/</link><guid isPermaLink="false">https://trungvose.comfront-end-editorial-style-guide/</guid><pubDate>Wed, 20 May 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;What&lt;/strong&gt;: A style guide on the &lt;strong&gt;standard format, spelling, and construction&lt;/strong&gt; of &lt;strong&gt;commonly used words and phrases&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why&lt;/strong&gt;: For &lt;strong&gt;clarity&lt;/strong&gt; and &lt;strong&gt;consistency&lt;/strong&gt; of our internal and external communications – from our user interface to our sales, marketing, and commercial docs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is our practical guide at &lt;a href=&quot;http://zyllem.com/&quot;&gt;Zyllem&lt;/a&gt;. Therefore, many of these standards are related to the logistic sector.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;style-guide&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#style-guide&quot; aria-label=&quot;style guide permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Style Guide&lt;/h2&gt;
&lt;p&gt;This guide is hosted on github. To read the full style guide, please visit:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide&quot;&gt;https://github.com/trungvose/front-end-editorial-style-guide&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I know this collection of standards is not perfect and still needs to improve. If you have any ideas, just &lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide/issues/new&quot;&gt;open an issue&lt;/a&gt; and tell me what you think.&lt;/p&gt;
&lt;p&gt;If you’d like to contribute, please fork the repository and make changes as you’d like. Pull requests are warmly welcome.&lt;/p&gt;
&lt;h2 id=&quot;table-of-contents&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#table-of-contents&quot; aria-label=&quot;table of contents permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Table of Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#capitalization&quot;&gt;Capitalization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#message-templates-common-errors-confirmations-etc&quot;&gt;Message Templates (Common Errors, Confirmations, etc.)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#common-word-spellings&quot;&gt;Common Word Spellings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#spelling-and-formatting-rule&quot;&gt;Spelling and Formatting Rule&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#spelling&quot;&gt;Spelling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#numbers&quot;&gt;Numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#date-and-time&quot;&gt;Date and time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#units-of-measurement&quot;&gt;Units of measurement&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#time&quot;&gt;Time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#distance&quot;&gt;Distance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#commas&quot;&gt;Commas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#common-grammar-dilemmas&quot;&gt;Common Grammar Dilemmas&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#1-plural-mishaps&quot;&gt;1. Plural Mishaps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#2-%22%22-versus-%22and%22&quot;&gt;2. ”&amp;#x26;” versus “And”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#3-one-word-versus-two-words&quot;&gt;3. One word versus two words&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/front-end-editorial-style-guide#contributing&quot;&gt;Contributing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Jekyll dark theme (minimal-mistake)]]></title><description><![CDATA[Once you go black you never go back :)]]></description><link>https://trungvose.comdark-theme-jekyll/</link><guid isPermaLink="false">https://trungvose.comdark-theme-jekyll/</guid><pubDate>Sat, 16 May 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Once you go black you never go back :)&lt;/p&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Developers love to work at night, where everything seems quieter than daylight. They love to see their IDE in black too. I used to work on VS 2015 default theme, which is blue. And my colleagues just didn’t understand why I don’t use the black theme :))
One day I decided to change to the black theme since then I couldn’t use the white theme anymore.&lt;/p&gt;
&lt;p&gt;Last year we saw a trend of a dark mode on numerous applications, from iOS 13 to Windows 10. Recently we also have Facebook dark mode. I think my blog should have a black theme for a long time but I was too lazy to do the customization.&lt;/p&gt;
&lt;p&gt;This post is to demonstrate how I write a few lines of custom SCSS and JS to make a dark mode for my blog. It is using &lt;a href=&quot;https://mmistakes.github.io/minimal-mistakes/&quot;&gt;minimal-mistakes&lt;/a&gt; Jekyll theme and hosted directly on Github.&lt;/p&gt;
&lt;h2 id=&quot;output&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#output&quot; aria-label=&quot;output permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Output&lt;/h2&gt;
&lt;p&gt;My blog is following the system configuration theme (in my use case, Windows 10). It is similar to how Chrome is doing its theme. I will add the toggle dark theme button soon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2ffaf0a30eb9983a5934ec1da850168b/01.gif&quot; alt=&quot;Jekyll dark theme (minimal-mistake)&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;dark-mode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dark-mode&quot; aria-label=&quot;dark mode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dark Mode&lt;/h2&gt;
&lt;p&gt;There are many examples of dark mode on the web. I often visit stackoverflow and found their dark theme is very good. They also published an article on &lt;a href=&quot;https://stackoverflow.blog/2020/03/31/building-dark-mode-on-stack-overflow&quot;&gt;Building dark mode on Stack Overflow&lt;/a&gt; with a clear explanation of how they started to layout and gradually generate the color scheme that they are currently using.&lt;/p&gt;
&lt;p&gt;So I don’t want to reinvent the wheel. I went direct to stackoverflow and took their color pallette for reusing.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-025&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #393939&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-050&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #3d3d3d&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-075&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #404345&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-100&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #4a4e51&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-150&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #555a5e&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-200&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #697075&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-300&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #7d848d&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-350&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #959ca3&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-400&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #9fa6ad&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-500&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #acb2b8&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-600&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #c4c8cc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-700&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #cfd2d6&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-750&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #dadee0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-800&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #e7e8eb&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$white-900&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f2f2f3&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I changed the background and text color of &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt;. Following by some minor changes for heading.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./variables&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token property&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$navy&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #2f3437&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;html &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all 0.1s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;body,
  .page__footer,
  .page__title &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all 0.1s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;.dark &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$navy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;body,
    .page__footer &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$navy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;h1,
    h2,
    h3,
    h4,
    h5,
    h6 &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.archive__item-title,
    .page__title &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.page__meta &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;.archive__item-description &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-700&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.author__urls.is--visible &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-050&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.author__bio &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.page__content &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token selector&quot;&gt;strong &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token selector&quot;&gt;u &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;border-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token selector&quot;&gt;.project-content &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token selector&quot;&gt;.project-title &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token selector&quot;&gt;.project-date &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;p &gt; code,
    a &gt; code,
    li &gt; code,
    figcaption &gt; code,
    td &gt; code &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-050&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.social-icons &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token selector&quot;&gt;.fa-map-marker,
      .fa-codepen,
      .fa-github &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;.pagination--pager &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$white-750&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally, using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;matchMedia&lt;/code&gt;&lt;/a&gt; to check if the dark theme is enabled to add the corresponding needed class to the &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt; tag. It also listens to the changes and updates the UI.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; DarkMode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DARK_MODE_QUERY&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;(prefers-color-scheme: dark)&apos;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DARK_CLASS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dark&apos;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isDark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matchMedia &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DARK_MODE_QUERY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matchMedia &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DARK_MODE_QUERY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;change&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isDarkMode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches
        fn &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isDarkMode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; html &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isDark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DARK_CLASS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;isDarkMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      isDarkMode &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DARK_CLASS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DARK_CLASS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;update-23-may-2020&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update-23-may-2020&quot; aria-label=&quot;update 23 may 2020 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update 23 May 2020&lt;/h3&gt;
&lt;p&gt;I added the button to toggle the dark theme manually, everything is working fine except the styling for the disquss section, because it is loaded through an &lt;code class=&quot;language-text&quot;&gt;iframe&lt;/code&gt;. You could refresh the page to have this section loaded properly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/808f7210bcd0d975d767a02a0fb3067e/02.gif&quot; alt=&quot;Jekyll dark theme (minimal-mistake)&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Understand and prevent the most common memory leaks in Angular application - Subscription unsubscribe]]></title><description><![CDATA[Remember to clean up your Rx subscriptions. In my experience, this is by far the most common cause of memory leaks in Angular applications]]></description><link>https://trungvose.comangular-common-memory-leak-use-case-observable/</link><guid isPermaLink="false">https://trungvose.comangular-common-memory-leak-use-case-observable/</guid><pubDate>Sat, 09 May 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - Remember to &lt;strong&gt;clean up your Rx subscriptions&lt;/strong&gt;. In my experience, this is by far the &lt;strong&gt;most common cause of memory leaks&lt;/strong&gt; in Angular applications.&lt;/p&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-common-memory-leaks?embed=1&amp;file=src/app/leaked-fix/child.leaked-fix.component.ts&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;A memory leak is one of the worst types of issues you can have. It’s hard to find, hard to debug, and often hard to solve. As a developer, it’s essential to know how memory leaks are created and how to deal with them. It could be criteria to differentiate between a good and an-average-developer. At a certain time, you started wondering how the memory is managed in the browser. This knowledge is especially important once your application reaches a certain size.&lt;/p&gt;
&lt;p&gt;Memory leaks occur in every programming language or framework, including Angular.&lt;/p&gt;
&lt;h2 id=&quot;javascript-memory-management&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#javascript-memory-management&quot; aria-label=&quot;javascript memory management permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Javascript Memory Management&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Low-level languages like C, have manual memory management primitives such as malloc() and free(). In contrast, JavaScript automatically allocates memory when objects are created and frees it when they are not used anymore (garbage collection). This automaticity is a potential source of confusion: it can give developers the false impression that they don’t need to worry about memory management.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Regardless of the programming language, the memory life cycle is &lt;strong&gt;pretty much always the same&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Allocate the memory you need&lt;/li&gt;
&lt;li&gt;Use the allocated memory (read, write)&lt;/li&gt;
&lt;li&gt;Release the allocated memory when it is not needed anymore&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The second part is explicit in all languages. The first and last parts are explicit in low-level languages but are mostly implicit in high-level languages like JavaScript. The majority of memory management issues occur at the third phase - to release the allocated memory. The most difficult aspect of this stage is determining when the allocated memory is no longer needed.&lt;/p&gt;
&lt;p&gt;For more detail on how JS memory allocation works, see &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management&quot;&gt;Memory Management&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;memory-leaks-in-angular&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#memory-leaks-in-angular&quot; aria-label=&quot;memory leaks in angular permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Memory leaks in Angular&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Memory leaks most often arise over time when components are re-rendered multiple times&lt;/strong&gt;, e.g through routing or by using the &lt;code class=&quot;language-text&quot;&gt;*ngIf&lt;/code&gt; directive, or you are listening to a WebSocket connection in the background and update the UI subsequently. For example, when a user works a whole day on our application without refreshing the browser and it is becoming increasingly slower.&lt;/p&gt;
&lt;p&gt;When the application started to get slower, we tended to reload the browser. By doing so, the browser release all the memory accumulated from the beginning, and our application started fresh again. But what if there are memory leaks happened and you didn’t even notice, so the QA team.&lt;/p&gt;
&lt;p&gt;To mimic a scenario, I created a setup with two components, &lt;code class=&quot;language-text&quot;&gt;ParentMemoryLeakedComponent&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;ChildMemoryLeakedComponent&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChildMemoryLeakedComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; OnDestroy &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;componentId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;counterSubscription&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counterSubscription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Counter &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;counter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;counter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; counter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnDestroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Counter &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; stopped at &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The child component has a timer to run &lt;strong&gt;every 1 second&lt;/strong&gt; and do &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt;. You will see the counter on the template as well. I also set the uniqueId for the component based on the current date by using &lt;code class=&quot;language-text&quot;&gt;new Date().getTime()&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;parent-leaked&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    &amp;lt;h2 class=&quot;mb-5&quot;&gt;Memory Leaked - example&amp;lt;/h2&gt;
    &amp;lt;button class=&quot;btn btn-primary mr-1&quot; (click)=&quot;relive()&quot;&gt;
      Relive
    &amp;lt;/button&gt;
    &amp;lt;button class=&quot;btn btn-danger&quot; (click)=&quot;destroy()&quot;&gt;
      Destroy
    &amp;lt;/button&gt;
    &amp;lt;child-leaked *ngIf=&quot;isAlive&quot;&gt;&amp;lt;/child-leaked&gt;
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ParentMemoryLeakedComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  isAlive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isAlive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;relive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isAlive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The parent has two buttons to toggle a flag. The child component will be displayed based on that flag.&lt;/p&gt;
&lt;p&gt;See my demonstration below, you will understand how easily we can create a memory leak in Angular by not unsubscribe from the subscription.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/0d4901a3944d03dcce001e25457af1e2/01.gif&quot; alt=&quot;Understand and prevent the most common memory leaks in Angular application&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The flow:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I click on &lt;code class=&quot;language-text&quot;&gt;Relive&lt;/code&gt; button, the child component is rendered&lt;/li&gt;
&lt;li&gt;After a couple of seconds, I click &lt;code class=&quot;language-text&quot;&gt;Destroy&lt;/code&gt;. The child component is disappeared from the UI, but the &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; keeps running. I can see two different component ids on the console&lt;/li&gt;
&lt;li&gt;I clicked &lt;code class=&quot;language-text&quot;&gt;Relive&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Destroy&lt;/code&gt; back end forth multiple times, the &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; keeps repeating multiple times. Think about it this way. If you click 100 times, you will see 100 &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; every second&lt;/li&gt;
&lt;li&gt;If you keep the browser open, your application keeps eating the computer memory up to a point the application became unsurprisingly slow&lt;/li&gt;
&lt;li&gt;You have to refresh the browser to free up the memory&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Stress test:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I opened the &lt;a href=&quot;https://developers.google.com/web/updates/2017/11/devtools-release-notes#perf-monitor&quot;&gt;Chrome Performance monitor&lt;/a&gt; and started to click &lt;code class=&quot;language-text&quot;&gt;Relive&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Destroy&lt;/code&gt; continuously. Noticed that the CPU usage and heap size is increasing so quickly. Up to a point when I clicked the button, but there was a long delay before the browser picked my action, It is only like 40 seconds after I started the test. I have to close the browser afterwards.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/3ea261aa758f469d7e64375c60435507/02.gif&quot; alt=&quot;Understand and prevent the most common memory leaks in Angular application&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The easiest way to fix the problem above is to clean the subscription when you destroy the component, or when you don’t need it anymore.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ngOnDestroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counterSubscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Counter &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; stopped at &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A single line of code &lt;code class=&quot;language-text&quot;&gt;this.counterSubscription.unsubscribe()&lt;/code&gt; could save you from a lot of problems in the future…&lt;/p&gt;
&lt;p&gt;See the below screenshot when I unsubscribe properly from the subscription. There are no memory leaked occurred.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/137753c0247b90fff0175ad92d621193/03.gif&quot; alt=&quot;Understand and prevent the most common memory leaks in Angular application&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;There are multiple ways to clean up Rx subscription. You could do as my example by assigning the subscription to a variable and then call &lt;code class=&quot;language-text&quot;&gt;unsubscribe()&lt;/code&gt; when you don’t need it. But if your component has many subscriptions as below code, your code will be ugly very soon.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LuckyComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; OnDestroy &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;mySubscription1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;mySubscription2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;mySubscription3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;mySubscription4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;mySubscription5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;mySubscription6&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token comment&quot;&gt;/*
      subscribing to multiple observables comes here
  */&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ngOnDestroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mySubscription1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mySubscription2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mySubscription3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mySubscription4&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mySubscription5&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mySubscription6&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I recommend to use:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ngneat/until-destroy&quot;&gt;until-destroy&lt;/a&gt;. A neat way to unsubscribe from observables when the component destroyed.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/42490265/3375906&quot;&gt;takeUntil() Angular component’s ngOnDestroy()&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I personally use &lt;code class=&quot;language-text&quot;&gt;until-destroy&lt;/code&gt; in my project at Zyllem.&lt;/p&gt;
&lt;h2 id=&quot;real-world-use-case&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#real-world-use-case&quot; aria-label=&quot;real world use case permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Real-world use case&lt;/h2&gt;
&lt;p&gt;Usually, we don’t write an application just to do &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; as my above example. I hope that simplicity can help you to understand the problem easier. Some of the real-world use cases that I have seen during development could be:&lt;/p&gt;
&lt;h3 id=&quot;1-you-subscribe-to-route-events-on-the-component-but-forgot-to-unsubscribe&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-you-subscribe-to-route-events-on-the-component-but-forgot-to-unsubscribe&quot; aria-label=&quot;1 you subscribe to route events on the component but forgot to unsubscribe permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. You subscribe to route events on the component but forgot to unsubscribe&lt;/h3&gt;
&lt;p&gt;It is very similar to my example, but instead of the timer run every second. You do as below&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChildDetailComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; OnDestroy &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;_route&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ActivatedRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_route&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onParamsChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;onParamsChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; carId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;carId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;//call API to get car detail by carId&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I saw my team always got that problem when we build master-detail UI where we have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A list of something. For instance, a list of cars&lt;/li&gt;
&lt;li&gt;Select one, display a car detail view on a different component&lt;/li&gt;
&lt;li&gt;When I open the detail view, I need the &lt;code class=&quot;language-text&quot;&gt;carId&lt;/code&gt; to get the detail data from the API&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If I open different cars with different Id, It creates a memory leak every time I instantiate the component. &lt;code class=&quot;language-text&quot;&gt;onParamsChange&lt;/code&gt; will be executed x times where x is the number of times &lt;code class=&quot;language-text&quot;&gt;ChildDetailComponent&lt;/code&gt; get initialized.&lt;/p&gt;
&lt;p&gt;To fix it, simply unsubscribe from the route event on component destroy.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChildDetailComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; OnDestroy &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;_route&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ActivatedRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_route&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onParamsChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;onParamsChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; carId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;carId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;//call API to get car detail by carId&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnDestroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-websocket-connections&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-websocket-connections&quot; aria-label=&quot;2 websocket connections permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Websocket Connections&lt;/h3&gt;
&lt;p&gt;Very similarly, WebSocket connections must always be closed when unused. I have prepared a simple Websocket component to display crypto prices. You can see the detail inside &lt;a href=&quot;https://stackblitz.com/edit/angular-common-memory-leaks?file=src%2Fapp%2Fwebsocket%2Fcoin-price.component.ts&quot;&gt;stackblitz&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Endpoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wss://ws.coincap.io/prices/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;coin-price&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    {{ id | titlecase }}: {{ (price$ | async) || &quot;loading...&quot; }}
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CoinPriceComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  @&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; price$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Subject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;webSocket&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WebSocket&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webSocket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebSocket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEndpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webSocket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MessageEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; price &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;price&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;price$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;ngOnDestroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//Remove this line to see memory leak happen.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webSocket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getEndpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;Endpoint&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;?assets=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If I remove this line &lt;code class=&quot;language-text&quot;&gt;this.webSocket.close()&lt;/code&gt;, you will see the memory leak.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/4c329ddafc0edbee2ecbc0acc5cc0e57/04.gif&quot; alt=&quot;Understand and prevent the most common memory leaks in Angular application&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-event-listeners&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-event-listeners&quot; aria-label=&quot;3 event listeners permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Event Listeners&lt;/h3&gt;
&lt;p&gt;Another common cause of memory leaks is DOM events that are never unregistered. Some folks may think that using Angular Renderer may take care of it, but that is only the cause if the events are defined in the template, just as with the async pipe.&lt;/p&gt;
&lt;p&gt;Let’s see a quick and common example of a component that registers a scroll listener on the body, without un-register the event:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ScrollComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;renderer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Renderer2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;renderer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;scroll&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updatePosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updatePosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* implementation */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This does, indeed, create a memory leak every time we instantiate &lt;code class=&quot;language-text&quot;&gt;ScrollComponent&lt;/code&gt; — so let’s fix it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ScrollComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; listeners &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;renderer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Renderer2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ngOnInit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; listener &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;renderer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&apos;scroll&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updatePosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;listeners&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;listener&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ngOnDestroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;listeners&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;listener&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updatePosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* implementation */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Memory Leaks are quite hard to find and debug&lt;/li&gt;
&lt;li&gt;Angular does a great job at managing memory; with that said, we need to watch out for open subscriptions (Observables, Subjects, NgRx Store Selections), DOM events, WebSocket connections, etc. &lt;strong&gt;Remember to unsubscribe from Rx subscription&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;resource&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource&quot; aria-label=&quot;resource permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resource&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management&quot;&gt;Memory Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/44454203/3375906&quot;&gt;Angular Renderer2 remove listener&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.bitsrc.io/debugging-memory-leaks-in-angular-4bc7b3eab569&quot;&gt;Debugging Memory Leaks in Angular&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[The different between [value] and [ngValue] when passing to select option]]></title><description><![CDATA[The different when using value and ngValue on Angular form]]></description><link>https://trungvose.comangular-select-option-value-ngvalue/</link><guid isPermaLink="false">https://trungvose.comangular-select-option-value-ngvalue/</guid><pubDate>Sat, 02 May 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - Set [value] on &lt;code class=&quot;language-text&quot;&gt;&amp;lt;option value=&quot;true&quot;&gt;Yes&amp;lt;/option&gt;&lt;/code&gt; will make our model/formcontrol return a string, which mean you receive a string &lt;code class=&quot;language-text&quot;&gt;&quot;false&quot;&lt;/code&gt;, instead of a boolean &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;. To solve it, use &lt;code class=&quot;language-text&quot;&gt;ngValue&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-value-ngvalue-option?embed=1&amp;file=src/app/app.component.ts&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;Recently I have to build a dropdown for a simple yes/no input, which output is equivalent to a boolean type. Obviously, we can always use a checkbox for that purpose.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;select&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ShowSLATimeWindows&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[(ngModel)]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;showSLATimeWindowValue&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[value]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;No: Hide the SLA time windows (only display the calculated time
    windows)&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;option&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[value]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Yes: Show the SLA time windows for each location in the app in addition to
    the calculated ones&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;option&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Answer: {{ showSLATimeWindowValue ? &quot;Yes&quot;: &quot;No&quot; }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Initially, I set the value of the ngModel to false &lt;code class=&quot;language-text&quot;&gt;showSLATimeWindowValue: boolean = false&lt;/code&gt;. It displays the value correctly. But as I started to change the value of the dropdown, the model value turned to be a string, either &lt;code class=&quot;language-text&quot;&gt;&quot;true&quot;&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;&quot;false&quot;&lt;/code&gt;. Not boolean &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; anymore. Therefore the condition to show &lt;code class=&quot;language-text&quot;&gt;showSLATimeWindowValue ? &quot;Yes&quot;: &quot;No&quot;&lt;/code&gt; is broken. Because both &lt;code class=&quot;language-text&quot;&gt;&quot;false&quot;&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;&quot;true&quot;&lt;/code&gt; will always be &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Truthy&quot;&gt;truthy&lt;/a&gt;, which mean in the context of boolean, they are always equal true.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/7109e6e35e74732039ae09da64edd93b/01.gif&quot; alt=&quot;The different between [value] and [ngValue] when passing to select option&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;As mentioned in the &lt;a href=&quot;https://angular.io/api/forms/NgSelectOption&quot;&gt;Angular documentation&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Input() ngValue: any&lt;/code&gt;: Tracks the value bound to the option element. Unlike the value binding, ngValue supports &lt;strong&gt;binding to objects&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Input() value: any&lt;/code&gt;: Tracks simple &lt;strong&gt;string values&lt;/strong&gt; bound to the option element. For objects, use the ngValue input binding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Based on the documentation, our example above works as expected.&lt;/p&gt;
&lt;h2 id=&quot;solution---to-use-ngvalue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution---to-use-ngvalue&quot; aria-label=&quot;solution   to use ngvalue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution - to use ngValue&lt;/h2&gt;
&lt;p&gt;In my case, I can use [ngValue] to set boolean values:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;select&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ShowSLATimeWindows&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[(ngModel)]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;showSLATimeWindowValue&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[ngValue]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;No: Hide the SLA time windows (only display the calculated time
    windows)&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;option&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  // value: false (as boolean)
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[ngValue]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Yes: Show the SLA time windows for each location in the app in addition to
    the calculated ones&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;option&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  // value: true (as boolean)
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;https://stackblitz.com/edit/angular-value-ngvalue-option?embed=1&amp;#x26;file=src/app/app.component.ts&quot;&gt;stackblitz&lt;/a&gt;, works perfectly fine. I received the boolean to display the answer correctly.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Lesson learn from Git branches]]></title><description><![CDATA[The goal of the game is to have as short-lived branches as possible (i.e. integrate often) while keeping the common branches (dev/patch) as stable as possible]]></description><link>https://trungvose.comlesson-learn-from-git-branches/</link><guid isPermaLink="false">https://trungvose.comlesson-learn-from-git-branches/</guid><pubDate>Sat, 25 Apr 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently our lead architect at &lt;a href=&quot;https://www.zyllem.com/&quot;&gt;Zyllem&lt;/a&gt; did a review on the collaborating process using Git. And the conclusion is, we are not great but not terrible.&lt;/p&gt;
&lt;p&gt;We need to do a better job of managing our development branches. The goal of the game is to have as &lt;strong&gt;short-lived&lt;/strong&gt; branches as possible (i.e. integrate often) while keeping the common branches (dev/patch) as stable as possible. Ex:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Version 1.0 (a new sprint)&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Story 1&lt;/u&gt;: As a user, I can calculate stuff (5d)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do the server work (dev1)&lt;/li&gt;
&lt;li&gt;Do the client UI (dev2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;u&gt;Story 2&lt;/u&gt;: As a user, I can do a new cool thing (1d)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do the server work (dev2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;u&gt;Bug 1&lt;/u&gt;: Something doesn’t work (5m / dev2)&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Bug 2&lt;/u&gt;: Fix another bug (30m / dev2)&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Bug 3&lt;/u&gt;: Fix yet another bug (1h / dev3)&lt;/p&gt;
&lt;p&gt;Currently, most of us would branch like this at the beginning of the sprint:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;development&lt;/li&gt;
&lt;li&gt;dev1-version-1.0 (based on development)&lt;/li&gt;
&lt;li&gt;dev2-version-1.0 (based on development)&lt;/li&gt;
&lt;li&gt;dev3-version-1.0 (based on development)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-problem&quot; aria-label=&quot;the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The problem&lt;/h2&gt;
&lt;p&gt;This branching strategy is simple but it leads to a lot of problems… typically (we have this all the time) dev3 needs the fix done for the bug2 to help to fix his bug3, since this fix is in the &lt;code class=&quot;language-text&quot;&gt;dev2-version-1.0&lt;/code&gt; branch he will merge that branch into his (dev3-version-1.0). But dev2 was working on both story 1 and 2 in his same branch, so his branch also includes some codes from the dev1 branch (the first work in progress commit of the server part of story 1). So now all branches are one big pile of half baked changes and no one understands what is going on (so we pull from each other until the thing compiles).&lt;/p&gt;
&lt;h2 id=&quot;the-consequence&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-consequence&quot; aria-label=&quot;the consequence permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The consequence&lt;/h2&gt;
&lt;p&gt;Now we have to wait until 5 issues are fully done to be able to merge this mess back to development (last day of the sprint), then QA will have to happen on Saturday. If we realize that Bug 2 will not take 30 minutes but 2 days we cannot postpone it (or we have to cherry-pick stuff in the messy branch). If we decide to postpone all 5 issues to the next sprint (version 1.1) now we have a set of branches with the wrong name (and trust me it’s easy to screw up a merge when we have 10 people doing that over 2-3 releases running in parallel).&lt;/p&gt;
&lt;h2 id=&quot;the-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution&quot; aria-label=&quot;the solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The solution&lt;/h2&gt;
&lt;p&gt;What we need to do is make a clear distinction between major stories and “nuggets”. Major stories are things that are either big or involving multiple devs (ex: Story 1, probably Story 2 unless it is straight-forward). For these, we need to create features branches. Depending on the complexity of the work for each of the devs collaborating on the branch, they can choose to collaborate directly on the branch or to branch off the common feature branch. Nuggets are the many small bugs that are not worth creating a whole branch for (just do one, call it &lt;code class=&quot;language-text&quot;&gt;dev1-version-1.0&lt;/code&gt; and fix those right in it, in a day). Branches for the above sprint could look like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;development&lt;/li&gt;
&lt;li&gt;story-1 (based on development)&lt;/li&gt;
&lt;li&gt;dev1-story1 (based on story-1) &amp;#x3C;—optional (depends on the complexity of server work)&lt;/li&gt;
&lt;li&gt;dev2-story1 (based on story-1) &amp;#x3C;—optional (same)&lt;/li&gt;
&lt;li&gt;dev2-version-1.0 &amp;#x3C;— for story 2 and the 2 bugs, since all can be done in 1-2 days easily&lt;/li&gt;
&lt;li&gt;dev3-version-1.0 &amp;#x3C;— will wait for dev2-version-1.0 to be merged on dev (no pull from dev2 branch)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This way if story 1 is delayed, no problem. If dev3 needs the thing from dev2 it should be available on development soon (worse case he can branch off dev2 branch and fix the issues there directly). The QA can start testing Story 2 and some bugs from the very beginning of the sprint…&lt;/p&gt;
&lt;p&gt;You can also create a new branch for each ticket you work on. But there could be many simple bug fixing that only required small changes. That’s why you should just combine a few of them and create a pull request at the end of the day. Checkout multiple branches could be time-consuming and you will have to clean them up on remote eventually.&lt;/p&gt;
&lt;p&gt;Anyway, next time you create a branch called &lt;code class=&quot;language-text&quot;&gt;my-name-some-version&lt;/code&gt; ask yourself if you do it for nuggets or not. Next time you need a commit from someone else’s branch, figure out how to get this to your branch only by pulling from your upstream (the branch yours is based off). If you keep pulling from all over the place, one day you will end up in a post-release email.&lt;/p&gt;
&lt;p&gt;Thanks &lt;a href=&quot;http://hiring.alsocan.la/&quot;&gt;Florian&lt;/a&gt; for contributing to this great practice.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Convert C# class to TypeScript interface]]></title><description><![CDATA[You could use an extension for VSCode for converting C# class to TypeScript class in a few seconds 🥰]]></description><link>https://trungvose.comconvert-csharp-class-to-typescript-class/</link><guid isPermaLink="false">https://trungvose.comconvert-csharp-class-to-typescript-class/</guid><pubDate>Wed, 15 Apr 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - Use extension &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=adrianwilczynski.csharp-to-typescript&quot;&gt;C# to TypeScript&lt;/a&gt; on VSCode to convert C# class to TS interface.&lt;/p&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;In the past few years, I always need to copy the C# class on the server-side code to a &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt; file. And then use VSCode to manually update the syntax to become an actual TS class/interface based on my need. Within Zyllem platform, the back end guys use inheritance and polymorphism extensively. In each concrete type, it might include another property in an abstract type that also has many concrete classes following this type. Therefore when I write the TS class/interface, it is time-consuming. For example, I have an abstract class for a custom property, and then some concrete classes for a specific type of custom property. The below code has been already converted from C# code.&lt;/p&gt;
&lt;p&gt;apCustomPropertyBase.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APCustomPropertyBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; staticId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; code&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomPropertiesTypes
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; level&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CustomPropertyCategory
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; CustomPropertiesTypes &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;TEXT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;NUMERIC&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;NUMERIC&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;OPTIONS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;OPTIONS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;MONEY&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;MONEY&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ID&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;apCustomPropertyMoney.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APCustomPropertyMoney&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APCustomPropertyBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; currencySymbol&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;apCustomPropertyOption.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APCustomPropertyOption&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APCustomPropertyBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; possibleOptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; APCustomPropertySubOption&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APCustomPropertySubOption&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; emptyKey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; exportValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;manually-converting-c-class-to-ts-equivalent&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#manually-converting-c-class-to-ts-equivalent&quot; aria-label=&quot;manually converting c class to ts equivalent permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Manually converting C# class to TS equivalent&lt;/h2&gt;
&lt;p&gt;As I mentioned above, to convert from C# to TS. I have to do it manually, and the work could take a lot of times if the complexity of the class increasing with inheritance and polymorphism. See the below gif for what I usually do. It has been much faster with VSCode multi cursor selection. It used to take me longer time before. In the gif, it took almost a minute to convert this simple class to TS.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/a2e9af94ea5f05e800026873239e29ce/01.gif&quot; alt=&quot;Convert C# class to TypeScript interface&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;Recently I found an extension - &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=adrianwilczynski.csharp-to-typescript&quot;&gt;C# to TypeScript&lt;/a&gt; on VSCode that make the process of converting the C# class to TS interface easier (only interface for now).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/772352394c7abbe4a7130247e587d016/02.gif&quot; alt=&quot;Convert C# class to TypeScript interface&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;See how I can convert it from C# code in just a click. Of course, not all the classes will be converted such as the factory, but it is good enough for the client-side. We just need the shape of data. Sometimes If I need to use the visitor pattern, I will decorate with additional methods that I need. It took only like 5 seconds to do so. Comparing with one minute above, I could say it is a big win.&lt;/p&gt;
&lt;p&gt;It is still in the early phase so that currently only supports converting to an interface, also only includes public, non-static properties &amp;#x26; fields - not methods, not private members. But still, it did save me sometimes. It also comes with some configuration as below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.export&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; controls exporting.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.convertDatesTo&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;&quot;string&quot;&lt;/code&gt; sets output type for dates. You can pick between string, Date and string Date.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.convertNullablesTo&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;&quot;null&quot;&lt;/code&gt; sets output type for nullables (int?) to either null or undefined.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.toCamelCase&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; toggles field name conversion to camel case.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.removeInterfacePrefix&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; controls whether to remove interface prefixes (IType -&gt; Type).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.generateImports&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; toggles simple import statements generation.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.quotationMark&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;&quot;double&quot;&lt;/code&gt; sets quotation marks for import statements &amp;#x26; identifiers (double or single).&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.useKebabCase&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; - use kebab case for file names.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;csharpToTypeScript.appendModelSuffix&quot;&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; - append “.model” suffix to file names.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Angular CDK Drag/Drop List inside a table (not Material Table) - Handle rows distorting width]]></title><link>https://trungvose.comangular-cdk-drag-drop-list-table/</link><guid isPermaLink="false">https://trungvose.comangular-cdk-drag-drop-list-table/</guid><pubDate>Fri, 03 Apr 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - When using Angular CDK Drag/Drop in a normal table using &lt;code class=&quot;language-text&quot;&gt;table&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;tr&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;td&lt;/code&gt;, you can manually set the column width to prevent rows distorting width. If you are using &lt;code class=&quot;language-text&quot;&gt;mat-table&lt;/code&gt; together with &lt;code class=&quot;language-text&quot;&gt;mat-row&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;mat-cell&lt;/code&gt;, you don’t have to worry about the rows distorting width issue.&lt;/p&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-cdk-drag-drop-bootstrap-table?embed=1&amp;file=src/app/app.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;In Zyllem, many places need to configure the order of each item, and this order will become very important when the runtime kicked in. In the beginning, our backend used MVC and Razor to build form, so usually, we will add one textbox to input the order number. Imagine you have a list of 10 items and you did the numbering. But then one day you need to change the number 5 to be the first one in the list so that you will have to manually update all the other 9 items as well. And it is also error prone because there is no validation, so there could be two items with the same number.&lt;/p&gt;
&lt;p&gt;Recently, we migrated some of the UI to client-side. We removed the order textbox, instead, we let the user drag and drop to change the order.&lt;/p&gt;
&lt;p&gt;To start using CDK Drag and drop, very simple. Start by importing &lt;code class=&quot;language-text&quot;&gt;DragDropModule&lt;/code&gt; into the &lt;code class=&quot;language-text&quot;&gt;NgModule&lt;/code&gt; where you want to use drag-and-drop features. You can now add the &lt;code class=&quot;language-text&quot;&gt;cdkDrag&lt;/code&gt; directive to elements to make them draggable. When the element is outside of a cdkDropList element, draggable elements can be freely moved around the page. You can add &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; elements to constrain where elements may be dropped.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are using &lt;code class=&quot;language-text&quot;&gt;mat-table&lt;/code&gt; together with &lt;code class=&quot;language-text&quot;&gt;mat-row&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;mat-cell&lt;/code&gt;, you don’t have to worry about the rows distorting width. It is supported out of the box for you.
See this working example of &lt;code class=&quot;language-text&quot;&gt;mat-table&lt;/code&gt; on &lt;a href=&quot;https://stackoverflow.com/a/58727454/3375906&quot;&gt;https://stackoverflow.com/a/58727454/3375906&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;1-configure-drag-and-drop-inside-a-table&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-configure-drag-and-drop-inside-a-table&quot; aria-label=&quot;1 configure drag and drop inside a table permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Configure drag and drop inside a table&lt;/h3&gt;
&lt;p&gt;Usually, we will have to use it in a table view. See the code below to enable drag and drop inside a table. It is to display a list of users to the screen.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;table&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;table&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;thead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;#&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;First&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Last&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Email&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Address&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;thead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tbody&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDropList&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;(cdkDropListDropped)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onDrop($event)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let user of users&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDrag&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDragLockAxis&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;drag-handle&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[ngTemplateOutlet]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dragHandleTmpl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          {{ user.order }}
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.first }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.last }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.email }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.address }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tbody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By simple adding &lt;code class=&quot;language-text&quot;&gt;cdkDropList&lt;/code&gt; into the &lt;code class=&quot;language-text&quot;&gt;tbody&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;cdkDrag&lt;/code&gt; to each &lt;code class=&quot;language-text&quot;&gt;tr&lt;/code&gt;. I simply mean I wanted each row to be able to move inside the body. &lt;code class=&quot;language-text&quot;&gt;cdkDragLockAxis=&quot;y&quot;&lt;/code&gt; to force the row to move upward and downward only, not freely. The result is displayed below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/5dcf828b747d6658393e5220bad49cd7/01.gif&quot; alt=&quot;Angular CDK Drag/Drop List inside table (not Material Table) - Handle rows distorting width&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-add-animation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-add-animation&quot; aria-label=&quot;2 add animation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Add animation&lt;/h3&gt;
&lt;p&gt;Now each row can be move, but it hasn’t looked great yet. We need to add a few more lines of CSS to add animation to the drag and drop event. Refer to this &lt;a href=&quot;https://material.angular.io/cdk/drag-drop/overview#styling&quot;&gt;documentation&lt;/a&gt; for the full list of CSS class that you can update.&lt;/p&gt;
&lt;p&gt;By only added these two lines, it looks much better now.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Animate items as they&apos;re being sorted. */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.cdk-drop-list-dragging .cdk-drag&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transform 250ms &lt;span class=&quot;token function&quot;&gt;cubic-bezier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.cdk-drag-preview&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; border-box&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 5px 5px -3px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0 8px 10px 1px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.14&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    0 3px 14px 2px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.12&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/5b26c2a80206904f5f560a1de88df246/02.gif&quot; alt=&quot;Angular CDK Drag/Drop List inside table (not Material Table) - Handle rows distorting width&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-fix-rows-distorting-width&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-fix-rows-distorting-width&quot; aria-label=&quot;3 fix rows distorting width permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Fix rows distorting width&lt;/h3&gt;
&lt;p&gt;But do you notice one thing? When you start dragging the row, The preview row has all of the columns stick together. I looked at the source and saw that Angular added &lt;code class=&quot;language-text&quot;&gt;cdk-drag-preview&lt;/code&gt; to the top level of body so that the style of the table was losing.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 621px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fc05faf108dfefc91803b97f65a67e0b/3075e/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB5klEQVQoz32O2YrdMBAF/f8fl4dASAiZGTLXiyRL1m7Jlu0K9w6TFdJQ1KEbjtSJUSK/T8z9E/1N8vSseX6e0dpijMFahw8e7x3e+0d23uHcL+77WSkWY+iOC2rMpElQYyKFzFYqZ7uzceyVth+0dnK0g2M7uI6T97mu6+EvHz8wvr68Fa42EEfNUStn9Vz7SiuJVjNHjdS6s+0n7e54f+T4WfZemGOgrJmunRdZJ1btKKUg7IEOJ9uaSc6z2EbNG8WuHFv742e/8z5dO2F1hTw7Qgh8vhW+DoXgFpw2CFVIvhBlZFv3t7L/0LW9sapIHmfCYvn0FJmEp8w9sl9YZKAoRxYW2v4v+93tka9toyvGEiaJv71gb0/MoyKIHvv6Dfkq0K8D5vsNNymCiTihWFRADgEjE1pEpptH9IHNW7qkLWoyzKPAiolgHVpo9CTRyiEGzSwMZvZImVGTRaoVKTKLXnF+x9qNmHaOVumysRgxs0wTVow441jkzCIESgbEsDBPFq0CSmbk6BjHRD9kpumNvk8MQ6KWSheUZxxX+ltGyYqShaHPSFFY5oQUCaMrzhScrdhlYzEFb+vDMWx4Vx+3fT/pikuE0VJdovrMFjI1ZFaXaTHQYqKlzJEy1MpV60//ne/8ANjDTDuLM9uPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Angular CDK Drag/Drop List inside table (not Material Table) - Handle rows distorting width&quot;
        title=&quot;Angular CDK Drag/Drop List inside table (not Material Table) - Handle rows distorting width&quot;
        src=&quot;/static/fc05faf108dfefc91803b97f65a67e0b/3075e/03.png&quot;
        srcset=&quot;/static/fc05faf108dfefc91803b97f65a67e0b/8ff5a/03.png 240w,
/static/fc05faf108dfefc91803b97f65a67e0b/e85cb/03.png 480w,
/static/fc05faf108dfefc91803b97f65a67e0b/3075e/03.png 621w&quot;
        sizes=&quot;(max-width: 621px) 100vw, 621px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I heard the cdk drag and drop work well with Material table. Because in material table, you can use the flexbox implementation. For a normal table, I came up with a solution to manually set the width of each column by CSS, it didn’t look the same as each row in the table, but the result is acceptable until Angular team provided us a proper solution for supporting table. Table column is a complicated topic, you can see all the rules of how the table column width is calculated in the &lt;a href=&quot;https://www.w3.org/TR/CSS21/tables.html&quot;&gt;W3 Table specs&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.col-xs&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.col-sm&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.col-md&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tbody&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDropList&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;(cdkDropListDropped)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onDrop($event)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let user of users&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDrag&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cdkDragLockAxis&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-xs&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;drag-handle&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[ngTemplateOutlet]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dragHandleTmpl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        {{ user.order }}
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-md&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.first }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-md&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.last }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-md&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.email }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;col-md&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ user.address }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tbody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And this is the result…&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/abe40bedf652896c91e6a2cb73349731/04.gif&quot; alt=&quot;Angular CDK Drag/Drop List inside table (not Material Table) - Handle rows distorting width&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular async validator to validate an input field with a backend API]]></title><description><![CDATA[Write a custom async validator to validate an input field with a backend API in Angular reactive form]]></description><link>https://trungvose.comangular-async-validator/</link><guid isPermaLink="false">https://trungvose.comangular-async-validator/</guid><pubDate>Fri, 20 Mar 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - Write a custom &lt;a href=&quot;https://angular.io/api/forms/AsyncValidator&quot;&gt;async validator&lt;/a&gt; to validate an input field with a backend API in Angular reactive form.&lt;/p&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-custom-async-validator?embed=1&amp;file=src/app/app.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;In Zyllem, a normal configuration form will have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Required title fields.&lt;/li&gt;
&lt;li&gt;A unique code name field. It will be auto-generated from the title if the user doesn’t input the code name.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Our best practice is to disable the submit button until the form is valid. This means the title has at least one character input, and the code is unique. Because the default required validator from Angular also accepts space as a valid character, so I have written a custom validator to make sure the input is valid after trimmed all the leading and trailing whitespace.&lt;/p&gt;
&lt;p&gt;But for the unique code name validator, the client will not be able to decide. The decision will be based on the server. As such, I asked the back end to provide an additional API to check the code name. Let assume there is a method &lt;code class=&quot;language-text&quot;&gt;validateCodeName(code: string)&lt;/code&gt; for that purpose. And somehow I have to glue it to the form. I could&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Call the &lt;code class=&quot;language-text&quot;&gt;validateCodeName&lt;/code&gt; on form submit. And then call the actual API to create/update the form based on the result of &lt;code class=&quot;language-text&quot;&gt;validateCodeName&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Manually call the &lt;code class=&quot;language-text&quot;&gt;validateCodeName&lt;/code&gt; each time the code name input changed and do the check to enable the submit button.&lt;/li&gt;
&lt;li&gt;Write a &lt;strong&gt;custom async validator&lt;/strong&gt; to validate the code name each time the code name has been changed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At first, I went for the first approach. But there are some behaviors which are not ideal and the QA started complaining about that. I will not go deep into these problems but it was a good time to switch to the approach (3) to write a custom async validator.&lt;/p&gt;
&lt;h2 id=&quot;set-up-the-form&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#set-up-the-form&quot; aria-label=&quot;set up the form permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Set up the form&lt;/h2&gt;
&lt;p&gt;There are two controls: title and code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;initForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sampleForm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_fb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;noSpaceValidation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    code&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;codeNameValidation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;async-validator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#async-validator&quot; aria-label=&quot;async validator permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Async Validator&lt;/h2&gt;
&lt;p&gt;You can see in the example, I create a mock API service with delay 300 to simulate the HTTP response in a real-world application. The code name will be invalid if entering &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;invalid&lt;/code&gt;. Otherwise, it will be valid. The killer function is the &lt;code class=&quot;language-text&quot;&gt;codeNameValidation&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt; &lt;span class=&quot;token function&quot;&gt;codeNameValidation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AbstractControl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Observable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ValidationErrors&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;switchMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;validateCodeName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isValid &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;isValid&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                isNotValid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Because the field is optional, if the user doesn’t enter any value It is still valid. That’s why I return &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; if the input is empty. Otherwise, go to the API server to check and update the form with the validity. Noted that there is the timer 200 at the beginning, it is the simple handling for debounce. You don’t want to check every time there is a value change, but only when the user stops typing. It enforces that the &lt;code class=&quot;language-text&quot;&gt;validateCodeName&lt;/code&gt; will not be called again until 200 milliseconds has passed without it being called previously. This approach I took from a &lt;a href=&quot;https://stackoverflow.com/a/45007974/3375906&quot;&gt;Stackoverflow answer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Also, I have a validator to check for any leading or trailing spaces.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt; &lt;span class=&quot;token function&quot;&gt;noSpaceValidation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AbstractControl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ValidationErrors &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        trimError&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Control has no value&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        trimError&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Control has leading whitespace&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        trimError&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Control has trailing whitespace&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;wait-for-async-validator-completed-before-submitting-form&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wait-for-async-validator-completed-before-submitting-form&quot; aria-label=&quot;wait for async validator completed before submitting form permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Wait for async validator completed before submitting form&lt;/h2&gt;
&lt;p&gt;In the example above, I only alert the form value for simplicity’s sake. But in a real-world app, you will most likely call an API with the form value to perform create/update. You have to take note that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Angular doesn’t wait for async validators to complete before firing ngSubmit. So the form may be invalid if the validators have not resolved.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In my form above, I have waited for the blur event on the title to set the code name. But the submit button will immediately enable after you entering the title if you don’t blur (press Tab, our click outside of the field) the title field.&lt;/p&gt;
&lt;p&gt;To overcome that problem, check this &lt;a href=&quot;https://stackoverflow.com/questions/49516084/reactive-angular-form-to-wait-for-async-validator-complete-on-submit&quot;&gt;answer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my actual code, it was how it looks.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// &amp;lt;form (ngSubmit)=&quot;formSubmitSubject$.next()&quot;&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formSubmitSubject$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Subject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formSubmitSubject$
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;tap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCodeName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;switchMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusChanges&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;startWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; status &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;PENDING&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; status &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;VALID&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;validationSuccessful &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;submitForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By doing so, you will have the confidence that the &lt;code class=&quot;language-text&quot;&gt;submitForm&lt;/code&gt; function will only be called after all the validators, including async validators, have been evaluated.&lt;/p&gt;
&lt;p&gt;I hope the Angular team will add this feature in the official version soon. There are many &lt;a href=&quot;https://github.com/angular/angular/issues/31021&quot;&gt;discussions&lt;/a&gt; going on.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular - Correct singular/plural form of a noun using custom pipe or NgPlural]]></title><description><![CDATA[Pluralization is a problem in its sphere. We need to always correctly define grammar in our apps based on the singular/plural value]]></description><link>https://trungvose.comangular-pipe-singular-plural/</link><guid isPermaLink="false">https://trungvose.comangular-pipe-singular-plural/</guid><pubDate>Sat, 15 Feb 2020 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pluralization is a problem in its sphere. We need to always correctly define grammar in our apps based on the singular/plural value. E.g&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;1 stop 5 stops&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some websites use the (s). So that it is up to the reader to remove (s) or add (s) when reading it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1 stop(s)
5 stop(s)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;1-custom-pipe&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-custom-pipe&quot; aria-label=&quot;1 custom pipe permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Custom Pipe&lt;/h2&gt;
&lt;p&gt;But as a front end developer who cares about the user experience, I always want to correct it for them.&lt;/p&gt;
&lt;p&gt;At the beginning, I ended up to do the check for every word, so the code to display singular/plural will looks like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Number &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; stops&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; numberOfStops &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; stop &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; numberOfStops &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;s&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But more and more code like that could be error-prone, what if my team member when he is too tired for coding, he will do the check as &lt;code class=&quot;language-text&quot;&gt;numberOfStops &gt;= 1&lt;/code&gt;. So that I wrote a simple &lt;code class=&quot;language-text&quot;&gt;plural&lt;/code&gt; pipe which takes a number as an argument and based on that to return the plural form of a word. As long as you are using this pipe, the result will be displayed as expected. No more funny check and error-prone.&lt;/p&gt;
&lt;p&gt;By default it will append &lt;code class=&quot;language-text&quot;&gt;s&lt;/code&gt; to the end of the word. But of course, you could specify the plural form of a noun because not all of them ended with &lt;code class=&quot;language-text&quot;&gt;s&lt;/code&gt; in a plural form. Such as &lt;code class=&quot;language-text&quot;&gt;bus&lt;/code&gt; will become &lt;code class=&quot;language-text&quot;&gt;buses&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;hero&lt;/code&gt; will become &lt;code class=&quot;language-text&quot;&gt;heroes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The code of &lt;code class=&quot;language-text&quot;&gt;PluralPipe&lt;/code&gt; looks like below, you can open the &lt;a href=&quot;https://stackblitz.com/edit/angular-singular-plural-form?embed=1&amp;#x26;file=src/app/plural.pipe.ts&quot;&gt;stackblitz&lt;/a&gt; to see it in action.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Pipe&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;plural&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CustomPluralPipe&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PipeTransform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; customPluralForm&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;s&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; customPluralForm &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To use it in the template, super easy.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Number of stops: {{ numberOfStops }} stop {{ numberOfStops | plural}}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Number of heroes: {{ numberOfHeroes }} hero {{ numberOfHeroes | plural:&quot;es&quot; }}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The problem becomes interesting when you have to do the plural form for some special words such as &lt;code class=&quot;language-text&quot;&gt;person&lt;/code&gt;, as it will come &lt;code class=&quot;language-text&quot;&gt;people&lt;/code&gt; in a plural form. Or &lt;code class=&quot;language-text&quot;&gt;child&lt;/code&gt;, will become &lt;code class=&quot;language-text&quot;&gt;children&lt;/code&gt;. I didn’t have the problem in a real project so what I can think of is to do a check like &lt;code class=&quot;language-text&quot;&gt;inputNumber &gt; 1 ? &quot;children&quot;: &quot;child&quot;&lt;/code&gt;. In the long run, you could consider to abstract that condition to a simple pipe that takes a numeric value and two input noun and then renders it accordingly.&lt;/p&gt;
&lt;h2 id=&quot;2-angular-ngplural&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-angular-ngplural&quot; aria-label=&quot;2 angular ngplural permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Angular NgPlural&lt;/h2&gt;
&lt;p&gt;You might not know that Angular has a built-in directive called &lt;a href=&quot;https://angular.io/api/common/NgPlural&quot;&gt;NgPlural&lt;/a&gt; for this need. You can use this directive for the use case I mentioned above with my awesome custom pipe code. But I still prefer my approach in a normal use case :)) I don’t know, I just get used to it.&lt;/p&gt;
&lt;p&gt;So basically, the NgPlural in action will look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;p [ngPlural]=&quot;numberOfStops&quot;&gt;
    Number of stops: {{ numberOfStops }
    &amp;lt;ng-template ngPluralCase=&quot;=1&quot;&gt;stop&amp;lt;/ng-template&gt;
    &amp;lt;ng-template ngPluralCase=&quot;&gt;1&quot;&gt;stops&amp;lt;/ng-template&gt;
&amp;lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Actually the above code only runs if with the first use case when the number is 1. If I increase it to 2, I got an error on the console &lt;code class=&quot;language-text&quot;&gt;ERROR Error: No plural message found for value &quot;2&quot;&lt;/code&gt;. Anyway I will come back to this directive If I need to use it in the future, maybe with i18n need.&lt;/p&gt;
&lt;h3 id=&quot;update-23-may-2020&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update-23-may-2020&quot; aria-label=&quot;update 23 may 2020 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update 23 May 2020&lt;/h3&gt;
&lt;p&gt;Based on Andy Bronson comments, you could try the code below for NgPlural. It should works very well. &lt;u&gt;Noted that the UI will still using the plural form if the value is 0&lt;/u&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;p [ngPlural]=&quot;numberOfStops&quot;&gt;
    Number of stops: {{ numberOfStops }
    &amp;lt;ng-template ngPluralCase=&quot;=1&quot;&gt;stop&amp;lt;/ng-template&gt;
    &amp;lt;ng-template ngPluralCase=&quot;other&quot;&gt;stops&amp;lt;/ng-template&gt;
&amp;lt;/p&gt;

Output
0 stops
1 stop
2 stops&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-singular-plural-form?embed=1&amp;file=src/app/plural.pipe.ts&quot;&gt;&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[How to export a table or an array in Angular to Excel file (xlsx)]]></title><description><![CDATA[Export data to Excel is very useful on the data list for nearly every web application. The export feature helps to download the data list in a table as a file format for offline use]]></description><link>https://trungvose.comangular-material-data-table-export-to-excel-file/</link><guid isPermaLink="false">https://trungvose.comangular-material-data-table-export-to-excel-file/</guid><pubDate>Fri, 16 Aug 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Export data to Excel is very useful on the data list for nearly every web application. The export feature helps to download the data list in a table as a file format for offline use. Excel format is ideal for exporting data in a file. Mostly the server-side method is used for export a large set of data to excel. But if you want a client-side solution to export table data to excel, it can be easily done using JavaScript.&lt;/p&gt;
&lt;p&gt;The client-side export functionality makes the web application user-friendly.&lt;/p&gt;
&lt;p&gt;Currently, I am building a brand new application with Angular. Many places need to have an export button to basically, download the table view into a xlsx file.&lt;/p&gt;
&lt;p&gt;I will use the &lt;a href=&quot;https://github.com/SheetJS/js-xlsx&quot;&gt;js-xlsx&lt;/a&gt; for the main exporting function.&lt;/p&gt;
&lt;p&gt;There are a few things to note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;TableUtil&lt;/code&gt; is a class that included all the necessary utility methods for the table. They are all static methods because we want to use them directly.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;exportToExcel&lt;/code&gt; function expects a string for the table id, and a desired file name you want to export. I also added the timestamp to the exported file name.&lt;/li&gt;
&lt;li&gt;The rest, &lt;code class=&quot;language-text&quot;&gt;js-xlsx&lt;/code&gt; will take care of for us. It is a great library and mature enough, for the excel creation itself. It also allows you to do much more with excel. For instance, you can add more worksheets to a workbook. If you need anything that I didn’t mention, please check the &lt;a href=&quot;https://github.com/SheetJS/js-xlsx&quot;&gt;library documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;It is purely client side method. So if you have server-side pagination with millions of records on your database and you want to export that millions of records data into an excel file. Do it on the server-side instead.&lt;/li&gt;
&lt;li&gt;For export with much more flexibility, check the second method where you can pass &lt;strong&gt;an array&lt;/strong&gt; into the export function and still get the excel file at the end. It will solve a lot of problems for you, e.g export a list of specific columns only, or when you are rendering the table using Material Flex table by using &lt;code class=&quot;language-text&quot;&gt;&amp;lt;mat-table&gt;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;lt;mat-header-cell&gt;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;lt;mat-cell&gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;I have tested this method on Angular 5 - 8, work perfectly fine. I haven’t had time to test with Ivy and Angular 9 but it should not be a big deal.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-material-table-export-excel-xlsx?embed=1&amp;file=app/tableUtil.ts&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;1-export-table-that-was-rendered-on-the-ui-to-excel-file-support-both-material-table-and-non-material-table-rendering-by-table-tr-and-td&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-export-table-that-was-rendered-on-the-ui-to-excel-file-support-both-material-table-and-non-material-table-rendering-by-table-tr-and-td&quot; aria-label=&quot;1 export table that was rendered on the ui to excel file support both material table and non material table rendering by table tr and td permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Export table that was rendered on the UI to Excel file (support both Material table and non Material table rendering by &amp;#x3C;table&gt; &amp;#x3C;tr&gt; and &amp;#x3C;td&gt;)&lt;/h2&gt;
&lt;p&gt;You can just simply give your table id (without the &lt;code class=&quot;language-text&quot;&gt;#&lt;/code&gt;) to the &lt;code class=&quot;language-text&quot;&gt;exportToExcel&lt;/code&gt; function, your table will be exported to an Excel file.&lt;/p&gt;
&lt;p&gt;tableUtil.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;xlsx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TableUtil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;exportToExcel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;tableId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; timeSpan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toISOString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ExportResult&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fileName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;prefix&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;timeSpan&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; targetTableElm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tableId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; wb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;table_to_book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetTableElm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Table2SheetOpts&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;sheet&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; prefix &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;fileName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.xlsx&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;table-basic-example.html&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;export-container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-raised-button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;primary&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;(click)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;exportTable()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Export
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;table&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ExampleTable&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-table&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[dataSource]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Position Column --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;matColumnDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-header-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderCellDef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;No.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matCellDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let element&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{element.position}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Name Column --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;matColumnDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-header-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderCellDef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Name&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matCellDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let element&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{element.name}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Weight Column --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;matColumnDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;weight&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-header-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderCellDef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Weight&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matCellDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let element&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{element.weight}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Symbol Column --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;matColumnDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;symbol&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;th&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-header-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderCellDef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Symbol&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matCellDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let element&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{element.symbol}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-header-row&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderRowDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;displayedColumns&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mat-row&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matRowDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let row; columns: displayedColumns;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;table-basic-example.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;table-basic-example&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;templateUrl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;table-basic-example.html&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TableBasicExample&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;displayedColumns&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;position&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;weight&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;symbol&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  dataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ELEMENT_DATA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;exportTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    TableUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exportToExcel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ExampleTable&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;important-note&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#important-note&quot; aria-label=&quot;important note permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Important Note&lt;/h3&gt;
&lt;p&gt;If you are rendering the Material flex table by using &lt;code class=&quot;language-text&quot;&gt;&amp;lt;mat-table&gt;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;lt;mat-header-cell&gt;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;lt;mat-cell&gt;&lt;/code&gt;. The above approach &lt;u&gt;will not work&lt;/u&gt;. You &lt;u&gt;have to use the second approach&lt;/u&gt; for exporting to excel file using an array.&lt;/p&gt;
&lt;p&gt;Or in the other words, this structure &lt;u&gt;will not work&lt;/u&gt; with our first approach to export a table.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mat-table&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[dataSource]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mat-elevation-z8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;matColumnDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mat-header-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderCellDef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Name &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mat-header-cell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mat-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matCellDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let element&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; {{element.name}} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mat-cell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;matColumnDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;symbol&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mat-header-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderCellDef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Symbol &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mat-header-cell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mat-cell&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matCellDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let element&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; {{element.symbol}} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mat-cell&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mat-header-row&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matHeaderRowDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;matColumns&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mat-header-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mat-row&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*matRowDef&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let row; columns: matColumns;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mat-row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mat-table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;2-export-an-array-to-excel-file&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-export-an-array-to-excel-file&quot; aria-label=&quot;2 export an array to excel file permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Export an array to excel file&lt;/h2&gt;
&lt;p&gt;With the first method, you have to take an array and then render it to the view and then finally, you export the HTML that was rendered. But &lt;code class=&quot;language-text&quot;&gt;js-xlsx&lt;/code&gt; also provides the built-in function to export the array directly to an excel file. Without the need to render it on the UI first.&lt;/p&gt;
&lt;p&gt;And because you could pass an array to &lt;code class=&quot;language-text&quot;&gt;js-xlsx&lt;/code&gt;. &lt;strong&gt;It will solve a lot of problems&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can select which column to include on the excel sheet by simple using Array.map() to transform your array&lt;/li&gt;
&lt;li&gt;Many comments said they want to remove the last column of the table, which usually has some action buttons. You don’t have to worry about that anymore if you export the array directly to the excel file.&lt;/li&gt;
&lt;li&gt;You could have an array of 1000 items but you only render 25 items per page with your client-side pagination. If you still want to click Export to have an excel file of all 1000 items, you are good to go. Just pass in the method your array and you’re all set.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I still use the array above which looks like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;position&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hydrogen&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;weight&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0079&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;symbol&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;H&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;position&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Helium&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;weight&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.0026&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;symbol&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;He&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are four properties, position, name, weight, and symbol. But let say I only want name and the symbol. I could easily transform the array using a map function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;onlyNameAndSymbolArr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Partial&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PeriodicElement&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token parameter&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;symbol&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And I have another utility to export an array&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;exportArrayToExcel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; any&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; sheetName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fileName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; wb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;book_new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ws &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json_to_sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;book_append_sheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ws&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sheetName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;XLSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;fileName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.xlsx&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that’s all for exporting your array into an excel sheet. Again, there are many other different options that &lt;code class=&quot;language-text&quot;&gt;js-xlsx&lt;/code&gt; support, you can take a look at their &lt;a href=&quot;https://github.com/SheetJS/js-xlsx&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Limit the number of simultaneous ajax requests]]></title><description><![CDATA[Hoư to limit the number of simultaneous requests to 100, and queue up the rest]]></description><link>https://trungvose.comlimit-the-number-of-simultaneous-ajax-requests/</link><guid isPermaLink="false">https://trungvose.comlimit-the-number-of-simultaneous-ajax-requests/</guid><pubDate>Fri, 21 Jun 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, we changed to use HTTP2 and our DevOps told me that there are some limitations of the current platform. In some specific area, there could be a few hundreds of request will be fired in parallel and could cause the system freeze. He wanted to limit the number of simultaneous requests to 100, and queue up the rest. I came up with a simple function for doing so.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; lazyLoadAjaxManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAX_PARALLEL_CALL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; queue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//to store the URL&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; activeCall &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;queueRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    queue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;checkQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onPromiseCompleteOrFailure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    activeCall&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;checkQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;queue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; activeCall &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAX_PARALLEL_CALL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; queue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      activeCall&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;

      &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;onPromiseCompleteOrFailure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;//TODO Write your custom logic here&lt;/span&gt;
          console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Success:&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;onPromiseCompleteOrFailure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Error:&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;addRequest&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; queueRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//To use -&gt; call addRequest(url). Usually for a large number of request, we will do it inside a loop. E.g&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// for (const userId of userIds) {&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     lazyLoadAjaxManager.addRequest(`https://example.com/user/${userId})&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// }&lt;/span&gt;

lazyLoadAjaxManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://example.com/request1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
lazyLoadAjaxManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://example.com/request2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
lazyLoadAjaxManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://example.com/request3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
lazyLoadAjaxManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://example.com/request4&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Basically, I first pushed all request url to a queue with a function called &lt;code class=&quot;language-text&quot;&gt;addRequest&lt;/code&gt; and call &lt;code class=&quot;language-text&quot;&gt;checkQueue&lt;/code&gt;. &lt;code class=&quot;language-text&quot;&gt;checkQueue&lt;/code&gt; checks to see if there are items in the queue and if the number of active requests is less than &lt;code class=&quot;language-text&quot;&gt;MAX_PARALLEL_CALL&lt;/code&gt;. If these conditions are met, it pops a request from the queue and turns it into a real AJAX request. Then it attaches a done handler to the request that decreases the active request count and calls &lt;code class=&quot;language-text&quot;&gt;checkQueue&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why I am still using &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;? Because it is the pure JS file will be served directly with no Babel or TypeScript compiled. So for safe, I am still using the old syntax.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Angular formArray/formGroup - Validate at least one checkbox was selected]]></title><description><![CDATA[I prefer to use FormGroup to populate the list of checkbox. For checking at least one checkbox was selected, write a custom validator.]]></description><link>https://trungvose.comangular-form-array-validate-at-least-one-checkbox-was-selected/</link><guid isPermaLink="false">https://trungvose.comangular-form-array-validate-at-least-one-checkbox-was-selected/</guid><pubDate>Sun, 09 Jun 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - I prefer to use FormGroup to populate the list of checkbox. For checking at least one checkbox was selected, write a custom validator.&lt;/p&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-validate-at-least-one-checkbox-was-selected?embed=1&amp;file=src/app/app.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;hr&gt;
&lt;p&gt;I often have the requirement to make sure there is at least one checkbox inside a list of checkboxes got selected on the UI. With the support of Angular Reactive form, you can archive it in a few lines of code.&lt;/p&gt;
&lt;p&gt;Most of the time, the list of checkboxes was populated on the server and I received it through API. But sometimes you will have a static set of the checkbox with your predefined value. With each use case, the corresponding FormArray or FormGroup will be used.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Basically &lt;code class=&quot;language-text&quot;&gt;FormArray&lt;/code&gt; is a variant of &lt;code class=&quot;language-text&quot;&gt;FormGroup&lt;/code&gt;. The key difference is that its data gets serialized as an array (as opposed to being serialized as an object in case of FormGroup). This might be especially useful when you don’t know how many controls will be present within the group, like dynamic forms.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For the sake of simplicity, imagine you have a simple create product form with&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One required product name textbox.&lt;/li&gt;
&lt;li&gt;A list of category to select from, required at least one to be checked. Assume the list will be retrieved from the server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First, I set up a form with only a product name formControl. It is a required field.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Validators&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since the category is dynamically rendering, I will have to add these data into the form later after the data was ready.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCategories&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;categories&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;categoriesFormArr&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buildCategoryFormArr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;categories&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;categoriesFormGroup&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buildCategoryFormGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;categories&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are &lt;strong&gt;two approaches&lt;/strong&gt; to build up the category list:&lt;/p&gt;
&lt;h2 id=&quot;1-form-array&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-form-array&quot; aria-label=&quot;1 form array permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Form Array&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;buildCategoryFormArr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;categories&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProductCategory&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;selectedCategoryIds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormArray &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; controlArr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; categories&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;category&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; isSelected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedCategoryIds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;controlArr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;atLeastOneCheckboxCheckedValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This &lt;code class=&quot;language-text&quot;&gt;buildCategoryFormArr&lt;/code&gt; will return me a FormArray. It also takes a list of selected values as an argument so If you want to reuse the form for edit data, it could be helpful. For building a new product form, it is not applicable yet. For instance, If I want &lt;code class=&quot;language-text&quot;&gt;category1&lt;/code&gt; to be selected by default, I can just pass it to the function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;buildCategoryFormArr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;categories&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;category1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The HTML is simple, just loop through the from array control and render it into the UI.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let control of categoriesFormArr?.controls; let i = index&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;[formControl]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;control&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    {{ categories[i]?.title }}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Noted that when you try to access the formArray values. It will look like &lt;code class=&quot;language-text&quot;&gt;[false, true, true]&lt;/code&gt;. To get a list of selected id, you will have to take based on the array index. It doesn’t sound good to me but it works.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;categoriesFormArraySelectedIds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;categories
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cat&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; catIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;categoriesFormArr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;control&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; controlIdx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; catIdx &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; controlIdx &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; control&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; cat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s why I came up using &lt;code class=&quot;language-text&quot;&gt;FormGroup&lt;/code&gt; for that matter&lt;/p&gt;
&lt;h2 id=&quot;2-form-group&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-form-group&quot; aria-label=&quot;2 form group permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Form Group&lt;/h2&gt;
&lt;p&gt;The difference of the formGroup is it will store the form data as the object, which required &lt;strong&gt;a key&lt;/strong&gt; and &lt;strong&gt;form control&lt;/strong&gt;. So it is a good idea to set the key as the categoryId and then we can extract it later.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;buildCategoryFormGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;categories&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ProductCategory&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;selectedCategoryIds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormGroup &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; group &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;validators&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;atLeastOneCheckboxCheckedValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  categories&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;category&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; isSelected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selectedCategoryIds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    group&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;category&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;formBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isSelected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; group&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let item of categories; let i = index&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;[formControl]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;categoriesFormGroup?.controls[item.id]&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;{%&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;raw&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;%}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;{{ categories[i]?.title }}{% endraw %}
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The value of the form group will look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;category1&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;category2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;category3&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But most often we want to get only the list of categoryIds as &lt;code class=&quot;language-text&quot;&gt;[&quot;category2&quot;, &quot;category3&quot;]&lt;/code&gt;. I also have to write a get to take these data. I like this approach better comparing to the formArray, because I could take the value of the form itself.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;categoriesFormGroupSelectedIds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;ids&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;categoriesFormGroup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controls&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;categoriesFormGroup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controls&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      ids&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ids&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;3-custom-validator-to-check-at-least-one-checkbox-was-selected&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-custom-validator-to-check-at-least-one-checkbox-was-selected&quot; aria-label=&quot;3 custom validator to check at least one checkbox was selected permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Custom validator to check at least one checkbox was selected&lt;/h2&gt;
&lt;p&gt;I made the validator to check at least X checkbox was selected, by default it will check against one checkbox only.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;atLeastOneCheckboxCheckedValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token parameter&quot;&gt;minRequired &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ValidatorFn &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;formGroup&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FormGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; checked &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

    Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;formGroup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controls&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; control &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; formGroup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controls&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;control&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        checked&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;checked &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; minRequired&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;requireCheckboxToBeChecked&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you’re all set. I hope it could help you get the idea. You could check again the &lt;a href=&quot;https://stackblitz.com/edit/angular-validate-at-least-one-checkbox-was-selected&quot;&gt;working example&lt;/a&gt; to see how they worked together. It is just a trivial problem but Angular team doesn’t provide us a really helpful document… It took me hours to just trying to get FormArray and FormGroup work for a list of the simple checkbox. It has been a few years since they introduced the Reactive Form but people keep asking about these simple &lt;a href=&quot;https://stackoverflow.com/q/40927167/3375906&quot;&gt;problems&lt;/a&gt;. Maybe I will switch to React or Vue very soon.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Restrict null and undefined via Non-Nullable-Types in TypeScript]]></title><description><![CDATA[null and undefined are the root of all evil. It often leads to runtime errors. It is easy to write code that will throw Error at runtime.]]></description><link>https://trungvose.comrestrict-null-and-undefined-via-non-nullable-types-in-typescript/</link><guid isPermaLink="false">https://trungvose.comrestrict-null-and-undefined-via-non-nullable-types-in-typescript/</guid><pubDate>Thu, 30 May 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;By default, null and undefined are assignable to all types in TypeScript.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trimText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; inputStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello I am Trung&apos;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;trimText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputStr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

inputStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;trimText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputStr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Uncaught TypeError: Cannot read property &apos;trim&apos; of null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;null-and-undefined-are-the-root-of-all-evil-it-often-leads-to-runtime-errors-it-is-easy-to-write-code-that-will-throw-error-at-runtime&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#null-and-undefined-are-the-root-of-all-evil-it-often-leads-to-runtime-errors-it-is-easy-to-write-code-that-will-throw-error-at-runtime&quot; aria-label=&quot;null and undefined are the root of all evil it often leads to runtime errors it is easy to write code that will throw error at runtime permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;null and undefined are the root of all evil. It often leads to runtime errors. It is easy to write code that will throw Error at runtime.&lt;/h2&gt;
&lt;p&gt;I have a variable name &lt;code class=&quot;language-text&quot;&gt;inputStr&lt;/code&gt; of type &lt;code class=&quot;language-text&quot;&gt;string&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The later code will call &lt;code class=&quot;language-text&quot;&gt;trimText&lt;/code&gt; on a null variable, the complier doesn’t complain but we know &lt;strong&gt;it will break at runtime&lt;/strong&gt;. Because there is no function &lt;code class=&quot;language-text&quot;&gt;trim&lt;/code&gt; on &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;To prevent it, TypeScript allows you to be explicit about what can and cannot be assigned a null or undefined. Added this line to &lt;code class=&quot;language-text&quot;&gt;compilerOptions&lt;/code&gt; inside your tsconfig.json file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;strictNullChecks&quot;: true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you see still the same code, but the editor warns me about not to assign &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;inputStr&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 500px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2282e63b88f96948eb3bb15fdac54aea/0b533/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 44.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABj0lEQVQoz42RS2/UMBSF8yuqeeRpJ5k8HduZJJ6HpkwFaIoQi0qsWLPh/+8/lJSKIirE4tM5V7LPvdLxRKmRbY9UPa2uOZ4s7rCnHRxlXbGzinyw5HWFSBKCICAMwz/wfR8pBG6a8JI4IakKhFEkTYW1Lbq3ZGWNagt621EUxV8hr4NffBRFeFJK4jDAX90Rru4I1qtl49b32WyfdWb+tPWDNy98HezFIiVIKzbuxvryxHp8TxhG1JmkSgVJFLKTCamMKLOYJI6el2y3C/PyzWaz+CUw35Xo4UBzuWE+PlG4K8ZarGoZjMZqzdQbhsny7t5hraWqKtpWLcy+6zRN0yISgbfLc3pjMFXByWpsXeP6PbpVGNWx7wxWdRz2I8fRMZoe07TLm9FYTKNw/bDMqUzxRNGQj1e6+xvd5QNq70jLmsx0FG5AZhlSSMSCWFTK9Nf828869+Hl/ZHrtx+cv36nOVzppzPu4ROq00RhSBzH/83S8i6TDLpi6AqGveX6+IWHx89MpzNJHP+z1bf4CeknB8hkh6VaAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        title=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        src=&quot;/static/2282e63b88f96948eb3bb15fdac54aea/0b533/01.png&quot;
        srcset=&quot;/static/2282e63b88f96948eb3bb15fdac54aea/8ff5a/01.png 240w,
/static/2282e63b88f96948eb3bb15fdac54aea/e85cb/01.png 480w,
/static/2282e63b88f96948eb3bb15fdac54aea/0b533/01.png 500w&quot;
        sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To still be able to assign &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;inputStr&lt;/code&gt;, you could define its type to be the union type of &lt;code class=&quot;language-text&quot;&gt;string | null&lt;/code&gt;. But when passed it down to the function, the editor again warns us that variable of type &lt;code class=&quot;language-text&quot;&gt;string | null&lt;/code&gt; will not be compatible to send down to the function. Because the function only accept string.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 550px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/853dd50f16c6c2af7ef2788d1307267a/dd45a/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7EAAAOxAGVKw4bAAABC0lEQVQoz52RzW7DIBCE/RwxYMC7YBMScOL8uWlvzfu/0VRgNUqlnHr4NDsjpF00jfMOcTdijBE+JoQQ4HoL0hq66ypGr6qNgjEdlFJv6boODTvGMHqwc7A+wBBDSolN26IVAkKIp4pWoK20q39DQ2OE22WQ8/DbBD+MYCIQUV3CxGB2q2eueD/AuZJx1ULJjTFolCybN6C+x+f9juV2w+P7gfk4I6cJOU9IKSPG3XO+XK6YpkP183yqlNw5j0apDkpKGEuIhwvyecF0uiKkBN5u0fdUL3nF2v6ZF31904Tgsd8PSHnC8vXA8fwBYobWBsZa6FKONi9q6td+57/ocmFpZ6W2JWUt5b/8ADljxyhJ5NQOAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        title=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        src=&quot;/static/853dd50f16c6c2af7ef2788d1307267a/dd45a/02.png&quot;
        srcset=&quot;/static/853dd50f16c6c2af7ef2788d1307267a/8ff5a/02.png 240w,
/static/853dd50f16c6c2af7ef2788d1307267a/e85cb/02.png 480w,
/static/853dd50f16c6c2af7ef2788d1307267a/dd45a/02.png 550w&quot;
        sizes=&quot;(max-width: 550px) 100vw, 550px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I changed the signature of the function to accept an union type of &lt;code class=&quot;language-text&quot;&gt;string | null&lt;/code&gt;. By doing so, I am be able to send &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; value to the function, but the editor again complains about the possibility of &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; value inside the function.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 400px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b2c2b1f01729ee7f53cd1d116dc51d9/e17e5/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.166666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABa0lEQVQoz32RUY6bMBRF2UaBAMYmQEiwsbEhGWUySVPN/ld0KpxOP1q1H0eyZL1zn68TOTjkcUYePW3fE8LI5WIJXnOYPE0/UB5aKnNCTCeatqWyI+o0MFlDVVVkWUae5xRFQVLViqKqKYWkrCqkFJFhODCHFescYV0xzqGdYxw1xs/4EAhhoet6jJnQWlPXkqQsCna7/Be7yJbYNHvsZDHGsC4rzjrG0xiH/ewJfsH7QN8fothMFqUakrIsX6J84yVN05S2bbl8/8Tfnsy3J9qfmX0gbJLjiH27Mb8/oqgdZw72/BJu7y6VRDpDbTXq2LMsmvvzk+vtB5M9cb0uWOvi1kIIRF0jRI2UijqeRWTrM1nPS+xO7BVVoxBS0nUNXduxlwpRC9QWKGUc2Bb4F9vHJFtammak31LyNGWXp2RZHi/z2Gnxu9v/yTaGYSAJQfPxEXjcA9oFpH6jP2qK4m/Bl/hPvrZ73O/8BF1O6ZBq2MecAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        title=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        src=&quot;/static/4b2c2b1f01729ee7f53cd1d116dc51d9/e17e5/03.png&quot;
        srcset=&quot;/static/4b2c2b1f01729ee7f53cd1d116dc51d9/8ff5a/03.png 240w,
/static/4b2c2b1f01729ee7f53cd1d116dc51d9/e17e5/03.png 400w&quot;
        sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So I did the check in place to prevent calling any method in null value. It is a simple ternary check whether if it is &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Truthy&quot;&gt;truthy&lt;/a&gt;.The editor looks happy.&lt;/p&gt;
&lt;p&gt;You can see before the ternary, type of &lt;code class=&quot;language-text&quot;&gt;inputStr&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;string | null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 440px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4687373390dd456bea6efa585f229fbe/48c0e/04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 26.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABAUlEQVQY002Q62rCQBSEfQ+zl2TNXszNdZMmBrFqKS2F0vd/mq8kIvhjfpyZYTgzmyJ0mLrHNCPGVwxDwzhFxreWOiZc1SJLQx4bTDpgbInqKmxsscYgpSTbbsmyLUIINkophJBIIVdCCkGWCbRSWOdxocJXNdYHrHM45yitw/r9qpXWsrOenQtorZ+BApnrB7RGKYkPgfvPH+P1k/PXL8P5nWkameeZNib6843Txzcx9VTxjSrNj0CtlhBFEVuK1GG6mqFvmC4XDqcbRZ7jmgM+OFKqOR47lids3bKvm1VfKkuRURQFG2stWqt1iwWL+YllBvl6v+iv/NLQGMP9euUfmW+R6hiaDLkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        title=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        src=&quot;/static/4687373390dd456bea6efa585f229fbe/48c0e/04.png&quot;
        srcset=&quot;/static/4687373390dd456bea6efa585f229fbe/8ff5a/04.png 240w,
/static/4687373390dd456bea6efa585f229fbe/48c0e/04.png 440w&quot;
        sizes=&quot;(max-width: 440px) 100vw, 440px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But after the question mark, its type was recognized as &lt;code class=&quot;language-text&quot;&gt;string&lt;/code&gt;. Great&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 440px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/af7eb0f82afb9834d16c8f782cf1f7cd/48c0e/05.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.83333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABBElEQVQY0y2QWVLDMBBEfY/Y2mWFWIkSyxuQCoSC/HD/8zxKIh9TMz1LT1c3dhhxxwUbZ9zLwDxFtu3CtibSNHM4Z2Rv0ZcTekq43mNSRKdIjBEhOtrdjrZt6bqOxliLkBIhVc1SSpRSKCWRSiGExDqH6wP+MOD3L7gQ8KHHe491Huf7GuW2sYVQiApkJZYVFyJVCTvGeWW7/7DdH6z3R8XLvJDzxCkvTNdP5ttXfdwYrRFGY8eEzWdsiqxLYnvfCKdzXXr7/mVc38jjwLpm9iHQD0fy65XeuypEa40xhiaEQNu1SK0QJZTCGFWHRW3xxfcBrQ1Siqdq8W/Psy7+lf7H7cYfRfqR5rcqkGUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        title=&quot;Restrict null and undefined via Non-Nullable-Types in TypeScript&quot;
        src=&quot;/static/af7eb0f82afb9834d16c8f782cf1f7cd/48c0e/05.png&quot;
        srcset=&quot;/static/af7eb0f82afb9834d16c8f782cf1f7cd/8ff5a/05.png 240w,
/static/af7eb0f82afb9834d16c8f782cf1f7cd/48c0e/05.png 440w&quot;
        sizes=&quot;(max-width: 440px) 100vw, 440px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So I hope with this example, you could start to apply strict null check in your project. Being explicit about null and undefined value will be super helpful to catch errors at the compile time before we actually run the code.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Error handling in JavaScript. Synchronous vs asynchronous code]]></title><description><![CDATA[Error handling in JavaScript has been always straightforward, at least for synchronous code]]></description><link>https://trungvose.comerror-handling-promise-async-await/</link><guid isPermaLink="false">https://trungvose.comerror-handling-promise-async-await/</guid><pubDate>Fri, 24 May 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Error handling in JavaScript has been always straightforward, at least for synchronous code. Consider the following example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeAnError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;A system error occurred.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;makeAnError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;error&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//Output&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//Error: A system error occurred.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The error got in the catch block as expected. Now let’s try with an asynchronous function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeAnError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;A system error occurred.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;makeAnError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;error&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above code is asynchronous because of &lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt;. What happens if we run it?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;Uncaught Error&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt; system error occurred&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
    at &lt;span class=&quot;token function&quot;&gt;makeAnError&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;anonymous&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
makeAnError @ &lt;span class=&quot;token constant&quot;&gt;VM86&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;async&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;anonymous&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; @ &lt;span class=&quot;token constant&quot;&gt;VM86&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This time the output is different. The error didn’t go through the catch block. It was free to propagate up in the stack.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That’s because try/catch only works with &lt;strong&gt;synchronous code&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Functions scheduled to run with setTimeout are executed in the main loop, outside the body of code that originated them&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;1-to-handle-errors-put-the-try-catch-inside-the-settimeout-handler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-to-handle-errors-put-the-try-catch-inside-the-settimeout-handler&quot; aria-label=&quot;1 to handle errors put the try catch inside the settimeout handler permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. To handle errors, put the try-catch inside the setTimeout handler:&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;A system error occurred.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;error&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-if-you-need-to-access-the-error-object-from-block-that-called-settimeout-use-promise&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-if-you-need-to-access-the-error-object-from-block-that-called-settimeout-use-promise&quot; aria-label=&quot;2 if you need to access the error object from block that called settimeout use promise permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. If you need to access the Error object from block that called setTimeout. Use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;Promise&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For extracting data from a Promise you need to chain a method called &lt;code class=&quot;language-text&quot;&gt;then&lt;/code&gt;. For error handling, use &lt;code class=&quot;language-text&quot;&gt;catch&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;makeAnError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

promise
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Ok &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Ouch &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//Output&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//Ouch Error: A system error occurred.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;asyncawait-and-error-handling-for-asynchronous-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#asyncawait-and-error-handling-for-asynchronous-code&quot; aria-label=&quot;asyncawait and error handling for asynchronous code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function&quot;&gt;async/await&lt;/a&gt; and error handling for asynchronous code&lt;/h3&gt;
&lt;p&gt;JavaScript is moving fast and every year we get constant improvements to the language. Promises seemed the arrival point but with ECMAScript 2017 (ES8) a new syntax was born: async/await.&lt;/p&gt;
&lt;p&gt;It is just a new way for writing asynchronous code based on Promises. Let’s make an example. We saw the promise above and doing &lt;code class=&quot;language-text&quot;&gt;then&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;catch&lt;/code&gt;. For async/await, &lt;strong&gt;try/catch&lt;/strong&gt; is supported.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;solvePromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; promise
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// or return the data with return data&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;solvePromise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//Output&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//Error: A system error occurred.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[TypeScript - Declare a function callback type]]></title><description><![CDATA[To define the function callback type. You could declare an interface that has a call signature. Or define a new type]]></description><link>https://trungvose.comtypescript-function-callback-type/</link><guid isPermaLink="false">https://trungvose.comtypescript-function-callback-type/</guid><pubDate>Sat, 11 May 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR To define the function callback type. You could declare an interface that has a call signature. Or define a new type.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Greeter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//OR&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//type Greeter = (message: string) =&gt; void;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Greeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hi!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-a-callback&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-a-callback&quot; aria-label=&quot;what is a callback permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is a callback?&lt;/h2&gt;
&lt;p&gt;A &lt;u&gt;callback&lt;/u&gt; is a term that refers to a coding design pattern where you can pass a function to another function. So that within the called function, it can “call back” the function you passed to it.&lt;/p&gt;
&lt;p&gt;Do you use a callback in JavaScript before ?!. Often seen in many scenarios. Let take a look at the example below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;message&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, how are you doing?&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hi!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;greeter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So there is a function &lt;code class=&quot;language-text&quot;&gt;sayHi&lt;/code&gt;, that accept another function as an argument and will execute this function when I start to call &lt;code class=&quot;language-text&quot;&gt;sayHi&lt;/code&gt;. The problem is I don’t know how the callback looks like, what is the type of its arguments. You can even call the function without any parameter, or multiple parameters. It doesn’t matter at all.&lt;/p&gt;
&lt;p&gt;In TypeScript, more often I would define an interface with a call signature like that.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Greeter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Greeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hi!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By declaring an interface that has a call signature named &lt;code class=&quot;language-text&quot;&gt;Greeter&lt;/code&gt; which accepts a string as an argument. We can start to see callback detail.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 761px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/77482233364b0ebe402c8bd77b2c16fa/8c857/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACWklEQVQ4y22T65KbOBCF/RYTg8BICImLwFwHXJ4MsTPJJJX9s+//MN8W8o4r5Zofp7qF0Ok+fdml1UBa9qT1M9I4VFuTLSOqa7DGIJOEKBREUUQcx3c8noUQzPPMLk013flKc74yXf+QlSVqbFFzTzH1ZNainjuyY00sIk/0QfboJ0nCzmQanRkPkxdsAVKtUVpjipw0TVFZ5u+VUh7bw8+y3ewuSQ7MLyvzt588rz9YLu+0/cg0jFzWC5fLlXEYaOcz8+Wd5/UNk5eIMPyUdJfmNaf1O/P1N6e3fxjPK8XQU54Xjt++cuxHctdSLxeqbsEYS1U5jDE3yX+RekJtcpbXleuv37z+eGMaByZXcKwdtiy9PCklUioSKe9nL9vX8IEwyzSpMRy7DlsU/md1iH13k0Tes/howCPixwwPcUQYBoTBniAICIKQMBQeIgwQIvTWPxYbBFEYfkrmCVVxRNkGaVuKsqTrKvre0bYVZTtRtBNuWJA65dBUxI0j71qUVOzDECEigi0BcZvVnTQFehjQywk7dLy8DKyvM9Vwol/fGdafLL/+ReclbVPSHB1D39AWFmc0tc1uNc8ztNbstFYkViNLS6xTjM3Ic0MkM3RRofMKUzW+nlYlWCUxMsGmCqsVJpWeeLN+sCMRsX/aEzw9EX55Yu/x5V67DeF+TxTdZAXiZr3/v9RNupcsBLuud0xTw3GYcOOJalio2x5XVb67W9RNym10JHrbHKX8Bm3fN//jfvN3zuXUdY5re6xrycqa0jny3PoiHw6H+9z9PYMfATb/to4HL/k/42mS6V0VE7gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;TypeScript - Declare a function callback type&quot;
        title=&quot;TypeScript - Declare a function callback type&quot;
        src=&quot;/static/77482233364b0ebe402c8bd77b2c16fa/8c857/01.png&quot;
        srcset=&quot;/static/77482233364b0ebe402c8bd77b2c16fa/8ff5a/01.png 240w,
/static/77482233364b0ebe402c8bd77b2c16fa/e85cb/01.png 480w,
/static/77482233364b0ebe402c8bd77b2c16fa/8c857/01.png 761w&quot;
        sizes=&quot;(max-width: 761px) 100vw, 761px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If we change the signature of function &lt;code class=&quot;language-text&quot;&gt;greeter&lt;/code&gt; to number instead of string, it will show you an error that the type doesn’t match. Now you have a strong type callback instead of just passing around function as we usually do in JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5af214fcdfdcdd2838849b2dd126e416/84bf8/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 40.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABZUlEQVQoz4WSa67bIBBGs5DEBr+NsTFgO7bTe5NUUbL/FZ0KolRqVak/PgZG8zgMHGStka2hMz3eafToUNOO60e2oeMy9BRSIEVKJgVpknA6nUiS5A8Fn3OOQ960iKGnGRTOahozo+cdZTyqqejbmrbMUWWOaSsyKX8XfNtTtB8dQkeZpuhOI0QWScJZCokQklRIZJaRZTkyy8nzgqIo/mlDk8PxeKQsS76/r2zbjrWOaZoZR8u6bizLGe+n6FOtYhgMfT9Ehb1znnleUKp7E4ZFCMH5vPJ6vbjd7lyvNx6PB5f9wvP55H7/GROddaznNTYN8t5HiJCrtX4XDIRpmlLXDdO0RLJt27DOY61nmhf2y49IG0g/NhQKsYEsKOTHK+e5pG4V43rDnr9wbmSZHaOfcesX3WhpZ482hsYYKqUoi5KqquOowvyKoqQsqwh26HWNGUfm7Y51S3y1z1cIAX9/j//pF5Im7iCPUj27AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;TypeScript - Declare a function callback type&quot;
        title=&quot;TypeScript - Declare a function callback type&quot;
        src=&quot;/static/5af214fcdfdcdd2838849b2dd126e416/d9199/02.png&quot;
        srcset=&quot;/static/5af214fcdfdcdd2838849b2dd126e416/8ff5a/02.png 240w,
/static/5af214fcdfdcdd2838849b2dd126e416/e85cb/02.png 480w,
/static/5af214fcdfdcdd2838849b2dd126e416/d9199/02.png 960w,
/static/5af214fcdfdcdd2838849b2dd126e416/84bf8/02.png 1162w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;generalize-the-callback-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#generalize-the-callback-type&quot; aria-label=&quot;generalize the callback type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Generalize the callback type&lt;/h2&gt;
&lt;p&gt;We could also use &lt;code class=&quot;language-text&quot;&gt;generic&lt;/code&gt; to generalize the use case of callback with one parameter as below. By doing so, you don’t have to define a new interface with a new name each time you need to use a callback with one parameter. Usually, the callback will not return any value, I default the &lt;code class=&quot;language-text&quot;&gt;T2&lt;/code&gt; type to &lt;code class=&quot;language-text&quot;&gt;void&lt;/code&gt;. But if you need to return a value from a callback function, you can specify the type for &lt;code class=&quot;language-text&quot;&gt;T2&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CallbackOneParam&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param1&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See the two below examples in action.&lt;/p&gt;
&lt;h3 id=&quot;void&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#void&quot; aria-label=&quot;void permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Void&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;message&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, how are you doing?&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CallbackOneParam&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hi!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;greeter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;with-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#with-type&quot; aria-label=&quot;with type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;With type&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;message&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, how are you doing?&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;greeter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CallbackOneParam&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; string&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hi!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;greeter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;alternative&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#alternative&quot; aria-label=&quot;alternative permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Alternative&lt;/h2&gt;
&lt;p&gt;There are other ways to do it, you can refer to this &lt;a href=&quot;https://stackoverflow.com/q/13137350/3375906&quot;&gt;question&lt;/a&gt;. One that worth mentioned is to define a type, but doing so will enforce the parameter name of this type and the function that you declare to be the same.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;type &lt;span class=&quot;token function-variable function&quot;&gt;MyHandler&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reference&quot; aria-label=&quot;reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/interfaces.html#function-types&quot;&gt;TypeScript handbook function type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/q/13137350/3375906&quot;&gt;https://stackoverflow.com/q/13137350/3375906&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Angular [(ngModel)] and debounce]]></title><description><![CDATA[In the use case of search, we don't want to hit the server endpoint every time user presses a key, it should flood them with a storm of HTTP requests. Basically, we only want to hit it once the user has stopped typing after sometimes (for instance 300ms) instead of with every keystroke.]]></description><link>https://trungvose.comangular-ngmodel-debounce/</link><guid isPermaLink="false">https://trungvose.comangular-ngmodel-debounce/</guid><pubDate>Tue, 30 Apr 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In AngularJS I can debounce a model by using ng-model options.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;ng&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;model&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{ debounce: 1000 }&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you don’t know what is debounce. Take a look at the simple definition below.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called. As in “execute this &gt; function only if 300ms milliseconds have passed without it being called.”
&lt;a href=&quot;https://css-tricks.com/the-difference-between-throttling-and-debouncing/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the use case of search, we don’t want to hit the server endpoint every time user presses a key, it should flood them with a storm of HTTP requests. Basically, we only want to hit it once the user has stopped typing after sometimes (for instance 300ms) instead of with every keystroke.&lt;/p&gt;
&lt;p&gt;In Angular, there are many different to archive it, but there is no built-in option as in AngularJS. I often leverage the capability of &lt;a href=&quot;http://reactivex.io/documentation/subject.html&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;RxJS Subject&lt;/code&gt;&lt;/a&gt; in order to archive that. Noted this method will trigger the &lt;strong&gt;change detection&lt;/strong&gt;. It is not efficient. Every keystroke, even though they are debounced, results in change detection running. Debouncing should not affect how often change detection runs. You can implement the DoCheck and see how many times ngDoCheck() is being called when you type into the input box.&lt;/p&gt;
&lt;p&gt;For a way that doesn’t trigger change detection, check out &lt;a href=&quot;https://stackoverflow.com/a/36849347/3375906&quot;&gt;this answer&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[(ngModel)]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;(ngModelChange)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;changed($event)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Search...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  modelChanged &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Subject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;searchResult$&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Observable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SearchResult&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; AppService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;modelChanged
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;debounceTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchResult$ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;changed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;modelChanged&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-ngmodel-debounce?embed=1&amp;file=src/app/app.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[Casting a JSON object to a TypeScript class]]></title><description><![CDATA[Not all the time we need to cast from JSON object to a class, but sometimes it is really helpful. Use class-transformer to transform JSON object to class instance]]></description><link>https://trungvose.comcast-parse-json-object-to-typescript-class/</link><guid isPermaLink="false">https://trungvose.comcast-parse-json-object-to-typescript-class/</guid><pubDate>Fri, 26 Apr 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TL;DR - Use &lt;a href=&quot;https://github.com/typestack/class-transformer&quot;&gt;class-transformer&lt;/a&gt; to transform JSON object to class instance.&lt;/p&gt;
&lt;h2 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h2&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-cast-json-to-typescript-class?embed=1&amp;file=src/app/app.component.ts&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;I started working with TypeScript about two years ago. Most of the time I read a JSON object from a remote REST server. This JSON object has all the properties of a TypeScript class. There is a question always buzz my head: &lt;strong&gt;How do I cast/parse the received JSON object to an instance of a corresponding class?&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Over time, I found this &lt;a href=&quot;https://github.com/typestack/class-transformer&quot;&gt;class-transformer&lt;/a&gt; library is super helpful. You can install this into your project and start using it from now on to see the difference. They supported nested property too so you don’t have to worry about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It is worth mentioning that not all the time we need to cast from JSON object to a class, but sometimes it is really helpful. As per the example above, If I don’t have the method &lt;code class=&quot;language-text&quot;&gt;getFullName&lt;/code&gt; in the instance of class, I could create a new util method that takes a plain &lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt; object as argument and return the expected result. In my projects, many times I used &lt;a href=&quot;https://refactoring.guru/design-patterns/visitor/typescript/example&quot;&gt;visitor pattern&lt;/a&gt; for handling different concrete classes, and it is required the instance of each class to be working. It won’t work for a plain object.&lt;/strong&gt;
&lt;strong&gt;The decision is yours. Put all properties and methods inside a single class is the encapsulation in Object-Oriented Programming (OOP) while Functional Programming treated everything as a function.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The below section was quoted from their readme.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In JavaScript there are two types of objects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;plain (literal) objects&lt;/li&gt;
&lt;li&gt;class (constructor) objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plain objects are objects that are instances of &lt;code class=&quot;language-text&quot;&gt;Object&lt;/code&gt; class.
Sometimes they are called &lt;strong&gt;literal&lt;/strong&gt; objects, when created via &lt;code class=&quot;language-text&quot;&gt;{}&lt;/code&gt; notation. E.g &lt;code class=&quot;language-text&quot;&gt;var obj = {}&lt;/code&gt;
Class objects are instances of classes with their own defined constructor, properties, and methods.
Usually, you define them via &lt;code class=&quot;language-text&quot;&gt;class&lt;/code&gt; notation.&lt;/p&gt;
&lt;p&gt;So, what is the problem?&lt;/p&gt;
&lt;p&gt;Sometimes you want to transform plain javascript object to the ES6 &lt;strong&gt;classes&lt;/strong&gt; you have.
For example, if you are loading a JSON from your backend, some API, or a JSON file. After you &lt;code class=&quot;language-text&quot;&gt;JSON.parse&lt;/code&gt;, it gives you a plain JavaScript object, not an instance of a class you have.&lt;/p&gt;
&lt;p&gt;For example you have a list of users that you received from the server:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Johny&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Cage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Ismoil&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Somoni&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Luke&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Dacascos&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And you have a &lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt; class defined on client side:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;
  firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
  age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;getFullName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;isAdult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You are assuming that you are downloading users of type &lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt; from &lt;code class=&quot;language-text&quot;&gt;users.json&lt;/code&gt; file and may want to write
following code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/api/users&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; User&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// you can use users here, and type hinting also will be available to you,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//  but users are not actually instances of User class&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// this means that you can&apos;t use methods of User class&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this code you can use &lt;code class=&quot;language-text&quot;&gt;users[0].id&lt;/code&gt;, you can also use &lt;code class=&quot;language-text&quot;&gt;users[0].firstName&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;users[0].lastName&lt;/code&gt;.
However you cannot use &lt;code class=&quot;language-text&quot;&gt;users[0].getFullName()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;users[0].isAdult()&lt;/code&gt; because “users” actually is
array of plain javascript objects, not instances of User object.
You lied to the compiler when you said that its &lt;code class=&quot;language-text&quot;&gt;users: User[]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So what to do? How to make a &lt;code class=&quot;language-text&quot;&gt;users&lt;/code&gt; array of instances of &lt;code class=&quot;language-text&quot;&gt;User&lt;/code&gt; objects instead of plain javascript objects?
The solution is to create new instances of User object and manually copy all properties to new objects.
But things may go wrong very fast once you have a more complex object hierarchy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alternatives&lt;/strong&gt;? Yes, you can use class-transformer. The purpose of this library is to help you to map you plain javascript
objects to the instances of classes you have.&lt;/p&gt;
&lt;p&gt;This library also great for models exposed in your APIs,
because it provides a great tooling to control what your models are exposing in your API.
Here is example how it will look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/api/users&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; realUsers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;plainToClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;User&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; users&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// now each user in realUsers is instance of User class&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you can use &lt;code class=&quot;language-text&quot;&gt;users[0].getFullName()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;users[0].isAdult()&lt;/code&gt; methods.&lt;/p&gt;
&lt;h3 id=&quot;this-is-the-result-as-my-consolelog-in-the-example-does-you-could-clearly-see-the-type-of-the-object&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#this-is-the-result-as-my-consolelog-in-the-example-does-you-could-clearly-see-the-type-of-the-object&quot; aria-label=&quot;this is the result as my consolelog in the example does you could clearly see the type of the object permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;This is the result as my console.log in the example does. You could clearly see the type of the object&lt;/h3&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 551px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/04ce3109bee88654aeee20923383cc75/db783/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.74999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAB/ElEQVQ4y42S2W7bMBRE9f8/1YciQNEiSdOgWZzEli1L1kJJXLRQVKRTUI4TIw9BBxhccSjOvUMw2F3eEl38YvfjkiQp2ewLNlFBVPQUsievu6WKt+rXi/bGTPq1RSiL0JYgP1Rk+5w8FhxyRRgWPDxHvGxrVuuIyz/3bMKE9PaBeL3n7mnL9d8n9i8Rye+/hOuIu1XM6qUmqzoCoR3CHJnLnkNq2GwLdolmn9Wso5w4k2RRTpLWhHFBGAuypCQND2ReSwTb2JwMLYX24w7kdUuSKuKdJs1bokPJNhFLnKpxlGagMo7auKV6njShekofuV09Yq6u6O7vaZShLAW1aZhmmJmZZv/xRq/MR560eZrw2D4/cvPzggAvvL4unKYJlSqysMS2r0yDZRoGvBXzh9lneqhKcIhCgmPfIz2sGtBpx9AOOKMZu+64/4XZqXoE5xvTPNGrHn1osO3AqCTTOL4fOv/3hHNTz+Bd9AJgtjHy2zXy+w36MaSzbrmKryKfM/jcaegtfdFihaFVBq0b+r7nf3EWeca5EZMVyPUOrVqEkIRhSCEErm0ZyxLXdUgpUUoxGsNYCmzbLo2NdmeR/XR2pEkKqnWIaTtsP2K0XgwHf1gI2qYhLwqUlDjfKM/ptKaWCiWHjwk9nXNY7bDNK7a3VFX5Hvf8JXwV+R/gXOmEh+9wkAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Casting a JSON object to a TypeScript class&quot;
        title=&quot;Casting a JSON object to a TypeScript class&quot;
        src=&quot;/static/04ce3109bee88654aeee20923383cc75/db783/01.png&quot;
        srcset=&quot;/static/04ce3109bee88654aeee20923383cc75/8ff5a/01.png 240w,
/static/04ce3109bee88654aeee20923383cc75/e85cb/01.png 480w,
/static/04ce3109bee88654aeee20923383cc75/db783/01.png 551w&quot;
        sizes=&quot;(max-width: 551px) 100vw, 551px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;working-with-nested-objects&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-with-nested-objects&quot; aria-label=&quot;working with nested objects permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working with nested objects&lt;/h3&gt;
&lt;p&gt;When you are trying to transform objects that have nested objects, its required to know what type of object you are trying to transform. Since Typescript does not have good reflection abilities yet, we should implicitly specify what type of object each property contains. This is done using &lt;code class=&quot;language-text&quot;&gt;@Type&lt;/code&gt; decorator.&lt;/p&gt;
&lt;p&gt;Let’s say we have an album with photos. And we are trying to convert album plain object to class object:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plainToClass &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;class-transformer&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Album&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number

  &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string

  @&lt;span class=&quot;token function&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Photo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;photos&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Photo&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Photo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number
  &lt;span class=&quot;token literal-property property&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; album &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;plainToClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Album&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; albumJson&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// now album is Album object with Photo objects inside&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Freeze screen in Chrome Debugger / DevTools for inspecting elements that disappear on hover/click (using setTimeout and Emulate a focused page)]]></title><link>https://trungvose.comfreeze-screen-debugger-chrome/</link><guid isPermaLink="false">https://trungvose.comfreeze-screen-debugger-chrome/</guid><pubDate>Tue, 09 Apr 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, I encountered a small CSS issue with a time picker component. To debug the code, I decided to use the Chrome debugger. However, I found that it wasn’t as straightforward as I expected…&lt;/p&gt;
&lt;p&gt;The UI of the component is triggered on click, but if there is any click event outside of the component, it disappears. This is also a common problem when inspecting elements like Bootstrap popovers or any component that displays UI on hover/click and hides it when clicked outside.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/a5099e701ed62f4efa65a5fb052e39b9/01.gif&quot; alt=&quot;Freeze screen in Chrome debugger&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;After coming across this &lt;a href=&quot;https://stackoverflow.com/q/17931571/3375906&quot;&gt;question&lt;/a&gt;, I discovered one promising solution. It’s surprising that I didn’t think of them initially!&lt;/p&gt;
&lt;h2 id=&quot;solution-1-using-settimeout-to-trigger-the-debugger&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-1-using-settimeout-to-trigger-the-debugger&quot; aria-label=&quot;solution 1 using settimeout to trigger the debugger permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution 1: Using setTimeout to trigger the debugger&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Run the following JavaScript code in the console: &lt;code class=&quot;language-text&quot;&gt;setTimeout(function(){ debugger }, 5000)&lt;/code&gt;. This will break into the debugger after 5 seconds.&lt;/li&gt;
&lt;li&gt;Trigger the UI element (e.g., hover over it) and wait until Chrome breaks into the debugger after 5 seconds.&lt;/li&gt;
&lt;li&gt;Go to the Elements tab in the inspector.&lt;/li&gt;
&lt;li&gt;Find your element and have fun modifying the CSS.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/6320250c92059eb586c3f159fdfdac5c/solution01.gif&quot; alt=&quot;Solution 1&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;solution-2-emulating-a-focused-page&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-2-emulating-a-focused-page&quot; aria-label=&quot;solution 2 emulating a focused page permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution 2: Emulating a focused page&lt;/h2&gt;
&lt;p&gt;I recently discovered a more straightforward method to freeze the screen in Chrome DevTools. This approach doesn’t require any JavaScript code and, surprisingly, has been available since &lt;a href=&quot;https://stackoverflow.com/a/64456947/3375906&quot;&gt;2020&lt;/a&gt;, which I hadn’t noticed before.&lt;/p&gt;
&lt;p&gt;If you switch focus from the page to DevTools, some overlay elements automatically hide if they are triggered by focus. For example, drop-down lists, menus, or date pickers. The &lt;code class=&quot;language-text&quot;&gt;Emulate a focused page&lt;/code&gt; option allows you to debug such an element as if it is in focus.&lt;/p&gt;
&lt;p&gt;To emulate a focused page:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a page with the element you want to debug, such as the &lt;a href=&quot;https://www.youtube.com/&quot;&gt;YouTube website&lt;/a&gt; with its search bar.&lt;/li&gt;
&lt;li&gt;On DevTools, open the Rendering tab, then check and clear the “Emulate a focused page” option.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/84a4df3381a4337d7fa6d6281b0ac38c/solution02.gif&quot; alt=&quot;Freeze screen in Chrome debugger / DevTools panel for inspecting elements that disappear on hover/click&quot; loading=&quot;lazy&quot;&gt;&lt;/p&gt;
&lt;p&gt;🎉 You can also find the same option under the &lt;code class=&quot;language-text&quot;&gt;:hov&lt;/code&gt; button on the action bar in Elements &gt; Styles.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fff12ee33794411b37cf5fe71d049b61/8ec0a/freeze-screen-01.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUCBAb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAGg0zr4dEw//8QAGhAAAgIDAAAAAAAAAAAAAAAAAgMAEAEEBf/aAAgBAQABBQLD2zRYR3za/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGxAAAgEFAAAAAAAAAAAAAAAAAQIQAAMSM4H/2gAIAQEABj8C2NT5MTNzkf/EAB0QAAEEAgMAAAAAAAAAAAAAAAEAEBExIUFxgfD/2gAIAQEAAT8hOBu5QPGSIldNXxtv/9oADAMBAAIAAwAAABDj7//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAICAgMAAAAAAAAAAAAAAAERACEQMUFhsf/aAAgBAQABPxA7UgOIh7PXdrbgIV4bnfiGqn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Freeze screen in Chrome debugger / DevTools panel for inspecting elements that disappear on hover/click&quot;
        title=&quot;Freeze screen in Chrome debugger / DevTools panel for inspecting elements that disappear on hover/click&quot;
        src=&quot;/static/fff12ee33794411b37cf5fe71d049b61/6a068/freeze-screen-01.jpg&quot;
        srcset=&quot;/static/fff12ee33794411b37cf5fe71d049b61/09b79/freeze-screen-01.jpg 240w,
/static/fff12ee33794411b37cf5fe71d049b61/7cc5e/freeze-screen-01.jpg 480w,
/static/fff12ee33794411b37cf5fe71d049b61/6a068/freeze-screen-01.jpg 960w,
/static/fff12ee33794411b37cf5fe71d049b61/644c5/freeze-screen-01.jpg 1440w,
/static/fff12ee33794411b37cf5fe71d049b61/0f98f/freeze-screen-01.jpg 1920w,
/static/fff12ee33794411b37cf5fe71d049b61/8ec0a/freeze-screen-01.jpg 2560w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Read more: &lt;a href=&quot;https://developer.chrome.com/docs/devtools/rendering/apply-effects#emulate_a_focused_page&quot;&gt;https://developer.chrome.com/docs/devtools/rendering/apply-effects#emulate_a_focused_page&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Angular render recursive view using *ngFor and ng-template]]></title><description><![CDATA[How to use ng-template to render view recursive in Angular]]></description><link>https://trungvose.comangular-recursive-view-render/</link><guid isPermaLink="false">https://trungvose.comangular-recursive-view-render/</guid><pubDate>Fri, 29 Mar 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We’re all familiar with the recursive view. One of the most common components that needed this technique is the nested navigation bar. The HTML structure and UI look like this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 2.1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 2.2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 2.3&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.2.1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.2.2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.2.3&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
              &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.2.3.1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
              &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.2.3.2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Section 3.3&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But how to make one with Angular?&lt;/p&gt;
&lt;h3 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h3&gt;
&lt;p&gt;Imagine I have a class &lt;code class=&quot;language-text&quot;&gt;NavigationModel&lt;/code&gt; that represents the data above.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NavigationModel&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; url&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NavigationModel&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So the trick is straightforward, you set up a &lt;code class=&quot;language-text&quot;&gt;ng-template&lt;/code&gt; which takes a list of &lt;code class=&quot;language-text&quot;&gt;NavigationModel&lt;/code&gt; as the parameter. Then render the template itself with &lt;code class=&quot;language-text&quot;&gt;children&lt;/code&gt; data if there are any children. Because &lt;code class=&quot;language-text&quot;&gt;children&lt;/code&gt; are also a list of &lt;code class=&quot;language-text&quot;&gt;NavigationModel&lt;/code&gt;. If you are not familiar with &lt;code class=&quot;language-text&quot;&gt;ngTemplateOutletContext&lt;/code&gt;, see &lt;a href=&quot;https://stackoverflow.com/a/45055768/3375906&quot;&gt;this question&lt;/a&gt; for more detail&lt;/p&gt;
&lt;p&gt;app.component.html&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;*ngTemplateOutlet&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;recursiveListTmpl; context:{ list: list }&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;#recursiveListTmpl&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;let-list&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;let item of list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ item.title }}
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;*ngIf&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item.children.length &gt; 0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ng-container&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;*ngTemplateOutlet&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;recursiveListTmpl; context:{ list: item.children }&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ng-template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;app.component.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Component &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@angular/core&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;app-root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;templateUrl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./app.component.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;styleUrls&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./app.component.scss&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AppComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NavigationModel&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 2.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 2.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 2.3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.2.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.2.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.2.3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.2.3.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.2.3.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.3.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Section 3.3.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you have it. Another solution is to create a new component/ directive that takes &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;NavigationModel[]&lt;/code&gt;. And do exactly the same thing as the &lt;code class=&quot;language-text&quot;&gt;ng-template&lt;/code&gt; example above.&lt;/p&gt;
&lt;h3 id=&quot;working-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#working-example&quot; aria-label=&quot;working example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Working Example&lt;/h3&gt;
&lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://stackblitz.com/edit/angular-render-recursive-view?embed=1&amp;file=src/app/app.component.html&amp;view=preview&quot;&gt;&lt;/iframe&gt;
&lt;br/&gt;</content:encoded></item><item><title><![CDATA[Skiing in Singapore - a coding diversion]]></title><description><![CDATA[Another interesting tree traverse problem that could land you a job 🤓]]></description><link>https://trungvose.comskiing-in-singapore/</link><guid isPermaLink="false">https://trungvose.comskiing-in-singapore/</guid><pubDate>Mon, 25 Mar 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hi there, it have been a while since my last post. Today I am re posting a quite interesting problem that I solved before.&lt;/p&gt;
&lt;p&gt;I read about this one on a &lt;a href=&quot;https://stackoverflow.com/jobs/162891/senior-front-end-engineer-redmart&quot;&gt;Senior Frontend JD&lt;/a&gt; on Stackoverflow. It looks promising so I decided to do it. You can read a step by step solution on my code. This is also my first time to use node API to read the local file.&lt;/p&gt;
&lt;h2 id=&quot;source-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code&quot; aria-label=&quot;source code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code&lt;/h2&gt;
&lt;p&gt;So basically it is a common tree traverse problem. I use the recursive function to solve it. I put my detail comment on the source code. Please help yourself. If there are any comments, just let me know.&lt;/p&gt;
&lt;p&gt;If you managed to run it on your local, you will get the number which is represent for a real email that you could send your resume to. You will get more advantage from doing so If you want to join Redmart.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/algorithm-training/blob/master/001.%20Skiing%20in%20Singapore/app.ts&quot;&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;ReadFile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;map.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; SkiingInSingapore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SkiingInSingapore&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//n m input&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;//array to hold the 2 dimensions array of number;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;//the expected result will be saved here&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;maxLength&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;maxDrop&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;firstLine&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;restData&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; nm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;splitString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstLine&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nm&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; nm&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; restData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;splitString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;restData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; x&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; y&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;//if the maxLength is greater then current value, no need to traverse&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxLength &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;traverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Max length &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxLength&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, max drop &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxDrop&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;traverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//consider as x y axis, instead of doing 4 if block. we can think about it as&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//current point [x,y]&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//go up [x, y + 1], go right [x + 1, y], go down [x, y - 1], go left [x - 1, y]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; xAxis &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; yAxis &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; k&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;//check if the moving is still inside the graph&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isInsideGraph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; xAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; xAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; yAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; yAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isInsideGraph &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; xAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; yAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

                &lt;span class=&quot;token comment&quot;&gt;//if can traverse and the current value is bigger the the next traverse point.&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;//set the length and keep the start point. to calculate the maxlength and drop later.&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;traverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; xAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; yAxis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; length &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;//current drop&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; drop &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; start &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;map&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxLength&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxLength &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxDrop &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; drop&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;//the use case where by length is the same but the drop is greater&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxLength &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxDrop &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; drop&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;maxDrop &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; drop&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test&quot; aria-label=&quot;test permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Test&lt;/h2&gt;
&lt;p&gt;There are 2 text files, named corresponding as.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://raw.githubusercontent.com/trungk18/algorithm-training/master/001.%20Skiing%20in%20Singapore/test.txt&quot;&gt;test.txt&lt;/a&gt; - the 4x4 matrix as you see on the below problem&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://raw.githubusercontent.com/trungk18/algorithm-training/master/001.%20Skiing%20in%20Singapore/map.txt&quot;&gt;map.txt&lt;/a&gt; - the 1000x1000 matrix&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Just change the name on &lt;strong&gt;app.ts&lt;/strong&gt; and you will get the result. I will add the unit test later.&lt;/p&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://geeks.redmart.com/2015/01/07/skiing-in-singapore-a-coding-diversion/&quot;&gt;Problem link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sometimes it’s nice to take a break and code up a solution to a small, fun problem. Here is one some of our engineers enjoyed recently. It’s called Skiing In Singapore.&lt;/p&gt;
&lt;p&gt;Well you can’t really ski in Singapore. But let’s say you hopped on a flight to the Niseko ski resort in Japan. Being a software engineer you can’t help but value efficiency, so naturally you want to ski as long as possible and as fast as possible without having to ride back up on the ski lift. So you take a look at the map of the mountain and try to find the longest ski run down.&lt;/p&gt;
&lt;p&gt;In digital form the map looks like the number grid below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;4 4
4 8 7 3
2 5 9 3
6 3 2 5
4 4 1 6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first line (4 4) indicates that this is a 4x4 map. Each number represents the elevation of that area of the mountain. From each area (i.e. box) in the grid you can go north, south, east, west - but only if the elevation of the area you are going into is less than the one you are in. I.e. you can only ski downhill. You can start anywhere on the map and you are looking for a starting point with the longest possible path down as measured by the number of boxes you visit. And if there are several paths down of the same length, you want to take the one with the steepest vertical drop, i.e. the largest difference between your starting elevation and your ending elevation.&lt;/p&gt;
&lt;p&gt;On this particular map the longest path down is of length=5 and it’s highlighted in bold below: &lt;strong&gt;9-5-3-2-1&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There is another path that is also length five: &lt;strong&gt;8-5-3-2-1&lt;/strong&gt;. However the tie is broken by the first path being steeper, dropping from 9 to 1, a drop of 8, rather than just 8 to 1, a drop of 7.&lt;/p&gt;
&lt;p&gt;Your challenge is to write a program in your favorite programming language to find the longest (and then steepest) path on this map specified in the format above. It’s 1000x1000 in size, and all the numbers on it are between 0 and 1500.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[npm - Check and update package if needed]]></title><description><![CDATA[Use npm outdated to check and upgrade package accordingly]]></description><link>https://trungvose.comnpm-update-package/</link><guid isPermaLink="false">https://trungvose.comnpm-update-package/</guid><pubDate>Thu, 24 Jan 2019 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was planning to upgrade Angular to the latest version for quite sometimes but since I have sticked to Angular 5 for so long, the upgrading process was complicated than I thought.&lt;/p&gt;
&lt;p&gt;Before going deeper, let me provide some of the background knowledge.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you install a package using &lt;code class=&quot;language-text&quot;&gt;npm install &amp;lt;packagename&gt; --save&lt;/code&gt;, the latest available version of the package is downloaded and put in the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; folder, and a corresponding entry is added to the &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; (and &lt;code class=&quot;language-text&quot;&gt;package-lock.json&lt;/code&gt; if you are using latest npm version) file that are present in your current folder.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; calculates the dependencies of the package and installs the latest available version of those as well.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;workflow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#workflow&quot; aria-label=&quot;workflow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Workflow&lt;/h3&gt;
&lt;p&gt;I believe the best way to upgrade the package is to do it manually. You don’t want to break your application by automated script that upgrade all the package to the version that you might not want. I usually do the upgrade process in two simple steps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identify out of date packages by &lt;a href=&quot;https://docs.npmjs.com/cli/outdated.html&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm outdated&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code class=&quot;language-text&quot;&gt;npm update&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; to get the latest versions of each package.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;upgrade-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#upgrade-process&quot; aria-label=&quot;upgrade process permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Upgrade process&lt;/h3&gt;
&lt;p&gt;As describe above, I will run &lt;code class=&quot;language-text&quot;&gt;npm outdated&lt;/code&gt; first. You can see the result as below.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/76de84b3c180a66bbc0fad98663704ee/553fd/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABzUlEQVQoz3WR647bIBhE/RobA3bANhjjS2LHiaPspVWltur7P8+pIN3Vtt39cQRCfMMwky3njX5e8aHHtW3CWodtLNbecc7hXPsp8f44DJxefpK1YWJ9+s5h2RjrmrYsMFonoaau06qUIs9zhBAfkguBEjluvpF108L8/INh2Th4hzOasizRWr9RFEUSjevHlElQugOZG2eOX3/RLRvBe6qqoiwUSkrkH+I+CkZez6Lj3W53dyklIn9AuonMdj39ckk5DsuZNgSq6Cp+U4g3gTgk1V14v98zTSPzPOO9p64baqMxw5ls8BZbGZq6wtmawxTo+kBTN4SuS47HruPxfOG2rhzHkWEcOR4H5vnIOE73IusK3Z/IvjUFnVRoIdFCcAw9zeCYTODlfOXlemXwPokPIeDbNrmKD8X8Yt7Rtdg9IOxE9qxLqkIh1D23OFj0DW3p2U4XHp8eMcbwsNul3PJ37b7GIePsaynPbcFRFzgVm5R436FCgy1b1uXM5bolN/JdMf/yl+AXqzgohU6vCfoQKPo6ObyuW3JYVQYh8nvbnxFbjl++rgZjDXlpEHuDHw7IoaM2A6f1xnp7QjcteamR+yrd+Z8KWZSIbuU3Kas8f/5hj/4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;npm update&quot;
        title=&quot;npm update&quot;
        src=&quot;/static/76de84b3c180a66bbc0fad98663704ee/d9199/01.png&quot;
        srcset=&quot;/static/76de84b3c180a66bbc0fad98663704ee/8ff5a/01.png 240w,
/static/76de84b3c180a66bbc0fad98663704ee/e85cb/01.png 480w,
/static/76de84b3c180a66bbc0fad98663704ee/d9199/01.png 960w,
/static/76de84b3c180a66bbc0fad98663704ee/553fd/01.png 1255w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;there-are-three-columns-that-we-might-interested-in&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#there-are-three-columns-that-we-might-interested-in&quot; aria-label=&quot;there are three columns that we might interested in permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;There are three columns that we might interested in.&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;current - show the current installed version.&lt;/li&gt;
&lt;li&gt;wanted - the maximum version of the package that satisfies the semver range specified in package.json. most of the time will satisfied minor.&lt;/li&gt;
&lt;li&gt;latest - the version of the package tagged as latest in the registry.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before running the command &lt;code class=&quot;language-text&quot;&gt;npm update @angular/common&lt;/code&gt;, my current &lt;code class=&quot;language-text&quot;&gt;@angular/common&lt;/code&gt; required &lt;code class=&quot;language-text&quot;&gt;^5.2.0&lt;/code&gt;. Basically it would match the version from &lt;code class=&quot;language-text&quot;&gt;5.2.0&lt;/code&gt; until &lt;code class=&quot;language-text&quot;&gt;5.9.9&lt;/code&gt; for instance. It must not greater than &lt;code class=&quot;language-text&quot;&gt;5.x.x&lt;/code&gt;, so the latest version &lt;code class=&quot;language-text&quot;&gt;7.2.14&lt;/code&gt; will not be matched. After running the command, it got the wanted version &lt;code class=&quot;language-text&quot;&gt;5.2.11&lt;/code&gt;. See the rule for versioning with tilde ~ and caret ^ below.&lt;/p&gt;
&lt;p&gt;For more information about &lt;code class=&quot;language-text&quot;&gt;npm update&lt;/code&gt;, you can refer to their documentation &lt;a href=&quot;https://docs.npmjs.com/cli/update.html&quot;&gt;here&lt;/a&gt;. As of &lt;code class=&quot;language-text&quot;&gt;npm@5.0.0&lt;/code&gt;, the npm update will change &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; to save the new version as the minimum required dependency.&lt;/p&gt;
&lt;p&gt;As I wanted to update to the latest version, I would run the command &lt;code class=&quot;language-text&quot;&gt;npm install --save&lt;/code&gt;. Refer to the photo below, the package.json file get updated with the latest version which is &lt;code class=&quot;language-text&quot;&gt;7.2.14&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca483d5017ade01eab54f8d4bffe29db/4fa52/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABV0lEQVQoz4WR2a7TMBRF8x91PGS0nThjE0hLRVG50gUEL0j8/58s5AQhQJV4WNqyz/aZnHhv6YaJ0nqyvMBk2Y42Bq01Wh9qTEYWY7/0T4wxlGXFOE4kqRD4bmZ+95FuXAhlQa0UuTbUdU1VVVhr96RCCNI0fYqUKbW1JEop2nFhub8yTCtjXVJpTbw3mdmrxy7i+XgonxJjIpUkWkrcuDDeP9FMK8FbTJajovGvDuR/iUUTrSRuWBhuL4S372nnNzS+ocoztFJIpXbj047EP6NLSRI/IK8stu2orcPbGmOO8U5CcDqddo3miFRHwqZpWJaZEMLvPUdNLnPPZQgE73ZT1wW2baWfR/qmY11XGu/ZzmcetxuX+Uxo2j1RCA4bm/Ae6xxlWZJ8mxw/5sDmPb1zTL7hfr0ybjMfhivfv3zl8+NBHwJ919G17V44UhQFeZ7vHCsQ/AQynuc6T3lzawAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;npm install&quot;
        title=&quot;npm install&quot;
        src=&quot;/static/ca483d5017ade01eab54f8d4bffe29db/d9199/02.png&quot;
        srcset=&quot;/static/ca483d5017ade01eab54f8d4bffe29db/8ff5a/02.png 240w,
/static/ca483d5017ade01eab54f8d4bffe29db/e85cb/02.png 480w,
/static/ca483d5017ade01eab54f8d4bffe29db/d9199/02.png 960w,
/static/ca483d5017ade01eab54f8d4bffe29db/4fa52/02.png 1241w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;for--tilde-and--caret-you-see-in-front-of-the-version-see-explanation-below&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#for--tilde-and--caret-you-see-in-front-of-the-version-see-explanation-below&quot; aria-label=&quot;for  tilde and  caret you see in front of the version see explanation below permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;For ~ (tilde) and ^ (caret) you see in front of the version, see explanation below&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The tilde matches the most recent minor version (the middle number). &lt;code class=&quot;language-text&quot;&gt;~1.2.3&lt;/code&gt; will match all 1.2.x versions but will miss &lt;code class=&quot;language-text&quot;&gt;1.3.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The caret, on the other hand, is more relaxed. It will update you to the most recent major version (the first number). &lt;code class=&quot;language-text&quot;&gt;^1.2.3&lt;/code&gt; will match any &lt;code class=&quot;language-text&quot;&gt;1.x.x&lt;/code&gt; release including &lt;code class=&quot;language-text&quot;&gt;1.3.0&lt;/code&gt;, but will hold off on &lt;code class=&quot;language-text&quot;&gt;2.0.0&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To upgrade all the Angular package, I repeated the process of doing &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; but with the list of other package as well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm install @angular/animations @angular/common @angular/compiler @angular/core @angular/forms @angular/http @angular/platform-browser @angular/platform-browser-dynamic @angular/platform-server @angular/router --save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[CSS Layout - Horizontal & Vertical Align]]></title><description><![CDATA[Vertically center in CSS has never been an easy problem]]></description><link>https://trungvose.comcentering-in-css-horizontal-vertical/</link><guid isPermaLink="false">https://trungvose.comcentering-in-css-horizontal-vertical/</guid><pubDate>Sun, 17 Jun 2018 16:00:00 GMT</pubDate><content:encoded>&lt;iframe height=&quot;300&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;centering-in-css-horizontal-vertical&quot; src=&quot;https://codepen.io/trungvose/embed/pbNLpM?default-tab=html%2Cresult&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&quot;https://codepen.io/trungvose/pen/pbNLpM&quot;&gt;
  centering-in-css-horizontal-vertical&lt;/a&gt; by Trung Vo (&lt;a href=&quot;https://codepen.io/trungvose&quot;&gt;@trungvose&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;p&gt;In CSS, several properties can be used to align elements horizontally and vertically. I hope these below tips will help you understand and able to align the element center horizontal and vertical.
Before going deeply, you can refer to these below source with good explanation and example.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.w3schools.com/css/css_align.asp&quot;&gt;http://www.w3schools.com/css/css_align.asp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/Style/Examples/007/center.en.html&quot;&gt;https://www.w3.org/Style/Examples/007/center.en.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/centering-css-complete-guide/&quot;&gt;https://css-tricks.com/centering-css-complete-guide/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;And this repository :)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;center-horizontal&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#center-horizontal&quot; aria-label=&quot;center horizontal permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Center Horizontal&lt;/h2&gt;
&lt;h3 id=&quot;1-inline-element-like-text-or-button&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-inline-element-like-text-or-button&quot; aria-label=&quot;1 inline element like text or button permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Inline element (like text or button..)&lt;/h3&gt;
&lt;p&gt;To simply center text inside a block element is using: &lt;strong&gt;text-align: center&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HTML&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;border-wrapper horizontal-inline-element-center&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Lorem Ipsum is simply dummy text of the printing and typesetting industry.
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Submit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CSS&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.horizontal-inline-element-center&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-block-element-div&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-block-element-div&quot; aria-label=&quot;2 block element div permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Block element (div)&lt;/h3&gt;
&lt;p&gt;To horizontally center a block element (like div), use &lt;strong&gt;margin: auto;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HTML&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;border-wrapper horizontal-block-element-center&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Lorem Ipsum is simply dummy text of the printing and typesetting industry.
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CSS&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.horizontal-block-element-center&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0 auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;center-vertical&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#center-vertical&quot; aria-label=&quot;center vertical permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Center Vertical&lt;/h2&gt;
&lt;h3 id=&quot;3-vertical-anything&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-vertical-anything&quot; aria-label=&quot;3 vertical anything permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Vertical Anything&lt;/h3&gt;
&lt;p&gt;This is a bit more tricky, but with &lt;strong&gt;position: absolute&lt;/strong&gt; and &lt;strong&gt;transform: translate(0, -50%)&lt;/strong&gt;, we will be able to align center vertically, even if we don’t know its height.
Translate negative 50% means move an element from its current position to middle of element’s height based on Y-axis&lt;/p&gt;
&lt;p&gt;HTML&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;wrap-col&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-wrap&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Pizza&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Pizza is a flatbread generally topped with tomato sauce and cheese and
        baked in an oven&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;wrap-background backstretch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pizza.jpg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CSS&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.wrap-col&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 33.333333%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.wrap-col::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;128&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 128&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 128&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.wrap-background&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 250px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.text-wrap&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; -50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.text-wrap a&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[How we handle time zone and locale at Zyllem]]></title><description><![CDATA[Using momentjs parseZone functionality for that purpose]]></description><link>https://trungvose.comhandle-timezone-client-server/</link><guid isPermaLink="false">https://trungvose.comhandle-timezone-client-server/</guid><pubDate>Fri, 27 Apr 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hi there, in the big application that supports user from multiple countries should always have the support for selecting timezone and even their locale somewhere. In Zyllem, as the SaaS company, this feature has been embedded inside the product since day 1.&lt;/p&gt;
&lt;p&gt;What we have applied so far is to follow the best practice to store UTC date/times in our database and display to the user in their local timezone. We’ll also store the time zone rather than the offset so we can support daylight savings time. In the post, I’ll only cover how we display the date in the correct timezone as the server deliver the result via an API and we have the client app to consume it.&lt;/p&gt;
&lt;p&gt;As described in the &lt;a href=&quot;https://stackoverflow.com/tags/timezone/info&quot;&gt;timezone tag wiki&lt;/a&gt;, there are two different styles of time zones.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Those provided by Microsoft for use with Windows and the .Net TimeZoneInfo class are identified by a value such as &lt;code class=&quot;language-text&quot;&gt;Singapore Standard Time&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Those provided by IANA in the TZDB are identified by a value such as &lt;code class=&quot;language-text&quot;&gt;Asia/Singapore&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I did a quick test with Moment timezone, and basically most client-side library using standard IANA time zones. Refer to the resource below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.iana.org/time-zones&quot;&gt;https://www.iana.org/time-zones&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Completed IANA timezone list: &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_tz_database_time_zones&quot;&gt;https://en.wikipedia.org/wiki/List_of_tz_database_time_zones&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Completed Microsoft timezone list: &lt;a href=&quot;https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones&quot;&gt;https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Client library the support IANA: &lt;a href=&quot;https://stackoverflow.com/a/15171030/3375906&quot;&gt;https://stackoverflow.com/a/15171030/3375906&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which is very different from our technology since we are using Microsoft and it come with their timezone (Microsoft’s one).&lt;/p&gt;
&lt;h3 id=&quot;so-we-came-out-with-two-solutions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#so-we-came-out-with-two-solutions&quot; aria-label=&quot;so we came out with two solutions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;So we came out with two solutions:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Server converts the timezone into IANA format and sends it to client using API. It doesn’t sound convenience because, with every single Date object, we need to include another string property as a timezone, and the string is big. E.g &lt;code class=&quot;language-text&quot;&gt;Singapore Standard Time&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Server converts the time into &lt;a href=&quot;http://en.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO 8601 format&lt;/a&gt;. Then the client will to parse it and display.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ISO 8601 meaning it should always include either a &lt;code class=&quot;language-text&quot;&gt;Z (for UTC values)&lt;/code&gt;, or an &lt;code class=&quot;language-text&quot;&gt;offset (for local values)&lt;/code&gt;. Therefore, you may take the user’s time zone into account, but the result you deliver should include the local offset so it is unambiguous.&lt;/p&gt;
&lt;p&gt;For example, you have the UTC value &lt;code class=&quot;language-text&quot;&gt;2018-04-28T03:00:00.00Z&lt;/code&gt;, then in &lt;code class=&quot;language-text&quot;&gt;Asia/Singapore&lt;/code&gt; it should be &lt;code class=&quot;language-text&quot;&gt;2018-04-28T11:00:00.00+08:00&lt;/code&gt;. And that’s what should be delivered over the API.&lt;/p&gt;
&lt;p&gt;On the client-side, you can render that value for the user with a library like moment.js. For example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; string &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; moment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;2018-04-28T11:00:00.00+08:00&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;LLL&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See more about localized format at &lt;a href=&quot;https://momentjs.com/docs/#/displaying/format/&quot;&gt;https://momentjs.com/docs/#/displaying/format/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So that we write a class to also handle both the locale and timezone. Basically, it will extract the locale from browser and display accordingly. For the getSafe function, refer to my &lt;a href=&quot;/blog/uncaught-type-error/&quot;&gt;previous post here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;timezone.ts&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getSafe &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./utils&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; moment &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;moment&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_FORMAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DD MMM YYYY, HH:mm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NUMERAL_DATE_TIME_ZONE_FORMAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;L LT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NAME_DATE_TIME_ZONE_FORMAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ll LT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NAME_DATE_ONLY_ZONE_FORMAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NAME_DATE_TIME_ZONE_FULL_FORMAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;llll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TIME_ONLY_FORMAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;LT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MomentTimezone&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;momentLocale&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setLocale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;momentLocale&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;momentLocale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSafe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;languages&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;language&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      moment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;momentLocale&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;//input format &quot;2018-04-28T11:00:00.00+08:00&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;formatDateInTimeZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; moment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;momentLocale &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NUMERAL_DATE_TIME_ZONE_FORMAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_FORMAT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseDateInTimeZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setLocale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; moment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So If I configure the browser language in Chinese, it will also display in the correct locale as well.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a88a7b91789e70f20c817def470c3bdc/63f05/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABgklEQVQoz22SzW7UMBRG88wF0Xam/BWQEEKiOx6EV0BsEYsuWDBlUdAkThpP4vgvjg+yYabpEEtnE98c+37XxZPTNavVM87Pn3J6dsF6/Zz1xQtev3nHq8uXnL39xOrjLY+vfnDyYTPjZsaGk6ufPHp/TTEMA+Po8d7jnGO/YozEOKG1Q9afGbrvhABhtIwJP8cQgkOUvymqqqIsK5RSWGvRWmeMMflbJRqUksi7byjVY61jmiaWltaGQkrJOI6LBdMUqZsm39xomYUhBGLcd3BPqu17RdG2LdqYfOox6aC6bnIcXT9Q1zXWWaapz3HMxak+dVRI2eKd/bvxnzAcbqi0QpkB7TTG3hDjuCys7loq2WGNyRnuZanocEPncG4kzqI7bvsg/Hor+PKrheAZQzgShtym9wGlKqxt/sn8YoZZWJYlTV3nH4UQD0gvoBIiT29QO7RWONthhg0hPJy0cZEuDWW73ZKkS6Q9IWp2ux15eNrgnMVak3Pd473DOkff9/wBxVjyM7QvQWwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How we store and display DateTime in the correct timezone and locale in Zyllem 01&quot;
        title=&quot;How we store and display DateTime in the correct timezone and locale in Zyllem 01&quot;
        src=&quot;/static/a88a7b91789e70f20c817def470c3bdc/d9199/01.png&quot;
        srcset=&quot;/static/a88a7b91789e70f20c817def470c3bdc/8ff5a/01.png 240w,
/static/a88a7b91789e70f20c817def470c3bdc/e85cb/01.png 480w,
/static/a88a7b91789e70f20c817def470c3bdc/d9199/01.png 960w,
/static/a88a7b91789e70f20c817def470c3bdc/07a9c/01.png 1440w,
/static/a88a7b91789e70f20c817def470c3bdc/29114/01.png 1920w,
/static/a88a7b91789e70f20c817def470c3bdc/63f05/01.png 2745w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/52b5f1c728f5d999451d7247704b3b18/5819f/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABIElEQVQoz1VR2W7DMAzr/3/ehqHA1ibdU5vbseTbKQepQ4IaoGVRlGDaJwCotaJumxzxxLG2bUMpBbW+aqLLuSiv2uehjiGg/T7jJMkyL3BEr8pz0wYRExEejw6LMcqt1qIfBsQY94HCC2otSDHiVEvBaBl3Q7gvVkHOwXuPEII2S5R8nmYd2Pe9YhxHMLPWBDL4JJtxATMHLBxgfUTKGfkfKaU9t8yYjYGxFisxVuY3rQ6Uq3OqMCErcj3e54AScMsC0/WwwwieJtAwYvPhTf+yvDI6Q+hXQm8I7MSu3634GOGNwfTxia5p0P1cMLY3DLdfUNPCD6Nq5NPUMoeoVgUkllPabWQ5l4LkHOjrDHttYH8uIInXBqFpkS2pRiz/AdOIHmguNDmuAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;How we store and display DateTime in the correct timezone and locale in Zyllem 02&quot;
        title=&quot;How we store and display DateTime in the correct timezone and locale in Zyllem 02&quot;
        src=&quot;/static/52b5f1c728f5d999451d7247704b3b18/d9199/02.png&quot;
        srcset=&quot;/static/52b5f1c728f5d999451d7247704b3b18/8ff5a/02.png 240w,
/static/52b5f1c728f5d999451d7247704b3b18/e85cb/02.png 480w,
/static/52b5f1c728f5d999451d7247704b3b18/d9199/02.png 960w,
/static/52b5f1c728f5d999451d7247704b3b18/5819f/02.png 1042w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The myth of the Genius Programmer]]></title><description><![CDATA[We, programmers are also human and we will make mistakes, the best thing we can do is try not to repeat them. Doing pair programing and code review helped us a lot to spot and prevent the problem before we ended up it on production. The collaboration is the key to success]]></description><link>https://trungvose.comthe-myth-of-the-genius-programmer/</link><guid isPermaLink="false">https://trungvose.comthe-myth-of-the-genius-programmer/</guid><pubDate>Tue, 06 Mar 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It has been almost a month after my last post. Actually, I was so busy and drunk with the Lunar New Year but also had a good time with my family and friends as well.&lt;/p&gt;
&lt;p&gt;Today I watched this video and found that’s so true. Listening to this talk made me feel much better about my work. We, programmers are also human and we will make mistakes, the best thing we can do is try not to repeat them. Doing pair programing and code review helped us a lot to spot and prevent the problem before we ended up it on production. The collaboration is the key to success.&lt;/p&gt;
&lt;p&gt;Summarizing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is no genius&lt;/li&gt;
&lt;li&gt;Lose the ego&lt;/li&gt;
&lt;li&gt;Criticism is not evil&lt;/li&gt;
&lt;li&gt;Embrace failure&lt;/li&gt;
&lt;li&gt;Iterate Quickly&lt;/li&gt;
&lt;li&gt;Be a small fish&lt;/li&gt;
&lt;li&gt;Be influenced&lt;/li&gt;
&lt;li&gt;Be vulnerable&lt;/li&gt;
&lt;li&gt;Tools&lt;/li&gt;
&lt;li&gt;Involve collaborators early, but not too (at the ‘sweet spot’)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don’t try to be a genius&lt;/li&gt;
&lt;li&gt;Collaborate early and often&lt;/li&gt;
&lt;li&gt;Pay attention to your tools&lt;/li&gt;
&lt;li&gt;Pay attention to timing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope you enjoy the video.&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;iframe class=&quot;iframe-full-w&quot; src=&quot;https://www.youtube-nocookie.com/embed/0SARbwvhupQ?controls=0&amp;amp;showinfo=0&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt;</content:encoded></item><item><title><![CDATA[Angular Tips: Avoiding unnecessary RxJS in vendor.ts]]></title><link>https://trungvose.comangular-tips-avoid-import-rxjs/</link><guid isPermaLink="false">https://trungvose.comangular-tips-avoid-import-rxjs/</guid><pubDate>Sun, 04 Feb 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;About 1.5 years ago, Angular was officially released the so-called Angular 2 after years in beta version. I was excited about this and even came and ask my boss to start building the new module in Angular, instead of current AngularJS. At the very beginning, Angular’s document was also very limited in both quantity and quality. We were even ended up with SystemJS in the production site. It was even worse than AngularJS performance because SystemJS need to load the whole bunch of Angular code in the browser before our app start rendering. But finally, they published a guide to pack Angular with Webpack.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://angular.io/guide/webpack&quot;&gt;https://angular.io/guide/webpack&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At the end, you just need one bundle output “app.js” to make your app work. The idea is to split our application in three parts: app, vendor and polyfills (this latter one is a requirement with ng2).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;polyfills: the polyfills needed to run Angular applications in most modern browsers.&lt;/li&gt;
&lt;li&gt;vendor: the third-party dependencies such as Angular, lodash, and bootstrap.css.&lt;/li&gt;
&lt;li&gt;app: the application code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Focusing on &lt;code class=&quot;language-text&quot;&gt;app&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;vendor&lt;/code&gt;, the idea is to have one bundle with only the application code and one bundle with only with vendor code.&lt;/p&gt;
&lt;p&gt;To make this separation to work, we need in &lt;code class=&quot;language-text&quot;&gt;vendor.ts&lt;/code&gt; to load our vendors too, so when webpack sees that we did an &lt;code class=&quot;language-text&quot;&gt;import &apos;foo&apos;&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;app.ts&lt;/code&gt; (or any .ts in our application) and in &lt;code class=&quot;language-text&quot;&gt;vendor.ts&lt;/code&gt;, will see that they are in common so they will get removed from the application code.&lt;/p&gt;
&lt;p&gt;So, if you for example comment out the line where you import the router in &lt;code class=&quot;language-text&quot;&gt;vendor.ts&lt;/code&gt; and you import the router only in application code, it will work but the entire router code will end in the final &lt;code class=&quot;language-text&quot;&gt;app.ts&lt;/code&gt; bundle instead of the vendor bundle.&lt;/p&gt;
&lt;p&gt;In short, when an import appears both in &lt;code class=&quot;language-text&quot;&gt;vendor.ts&lt;/code&gt; and application code, that 3rd party library will be removed from the application code and put in a vendor bundle alone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is only the first step to achieve better things like proper caching so if you update only the app code and not the vendor, you won’t force the client to download the vendors again.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So I followed the mentioned guide, and put this block of code inside &lt;code class=&quot;language-text&quot;&gt;vendor.ts&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Angular&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/platform-browser&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/platform-browser-dynamic&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/core&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/common&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/http&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@angular/router&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// RxJS&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And you can see how big the &lt;code class=&quot;language-text&quot;&gt;vendor.ts&lt;/code&gt; is, even with minification, almost 1mb. And the rxjs is taken up &lt;strong&gt;~250kb&lt;/strong&gt;, which is equivalent to &lt;strong&gt;26%&lt;/strong&gt; of my vendor bundle.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 547px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c0eece4d7940d539045951476cc81d8f/977f7/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABi0lEQVQoz12R227aUBBFD6KEGAKkaaA0VknSJg13Y/uc4yu+kBCTCLlS1cc+9v9/YVWGtqr6sKTZ0p7RnhlRd2NOw0fawQbDz2l5GWfR00E3ViFiqhAzfeTf+n89VWQ/fiLMrOB2u2dZfud+VzLavHJflAzWW94sfMTYPfK3WR35oycSMZaIB5dB/Iy48HOGScHnouQyfMRw1gdTla7CcGO6XkZt7iEm1SB95Heqk1XImUpoq4SOThH1laStFU1b0V8nDJKEthUySneM0oKOHWGut/RkhCGPXsNR1Cea2ljS9zJmu5LrtGAQ5IiTmWToOMyiOTdqwZVr8X4lWVRa2jS+OLxdBlz5McPcxXxSvAsUt9LiTs0Z+Rne/hs34YbmnYWozRVdXzKIbPqhS9d16Tg+D5uMD0FMbSIxrJCejugmkvNc0fIVHcvl3F5x6WdMX75ynRVcVCs3pKSVapqxphlp6krR83PM/JV+vD08pL4MMHREK1G0U81pWD1DH25a3e3T856PmxfMfMcvK2rQwu2tpLgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;angular tips avoid import rxjs&quot;
        title=&quot;angular tips avoid import rxjs&quot;
        src=&quot;/static/c0eece4d7940d539045951476cc81d8f/977f7/01.png&quot;
        srcset=&quot;/static/c0eece4d7940d539045951476cc81d8f/8ff5a/01.png 240w,
/static/c0eece4d7940d539045951476cc81d8f/e85cb/01.png 480w,
/static/c0eece4d7940d539045951476cc81d8f/977f7/01.png 547w&quot;
        sizes=&quot;(max-width: 547px) 100vw, 547px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/506ab21e78ba1226b817519678aaf51e/da994/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABcUlEQVQoz21S226DMAzl/79ue1hXJCao1hJCITeSEG49k9OmolotWQbb58S3bAwB3vuo4zhimiZMU4BzLvqM0SiKAr0QmOc5xilv9B7Tw3rnEMhai2yZZ6zrGpWS+77H9drBDCYCicRaixACjDEQUkZw6HqopgE/nSBqBtcLyLJEZo0Gye12w7Is0NpE4LZtTz8JxajqwVosY4BsOL7zPBZAvonivEWWQAm4l+RLjz1lXaEajmOeQysFpRSWbcMs5CvhO90TRt+DkJUVvg4HKCnjaKx3kL/n/4T76vaENMs9Ic3ueDzCaIVhGEDLfWn5HdHeR0vbC6vuFbphiLN9EtLwaYu85fElAlJ7Qgi0vI0xaolzDikllNbgdY3ufEF1OoE3DVreQCoFfanvFaYNUlv7f9IQpnhO6U6ddzBCxC2fGUNZVagZQ9t1YMUPsnTUdGcEoO9UVXgcfbrDcfRwlOsczO8Zqq6haxYtrypcPj7xBxZWBXOqMKiqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;angular tips avoid import rxjs&quot;
        title=&quot;angular tips avoid import rxjs&quot;
        src=&quot;/static/506ab21e78ba1226b817519678aaf51e/d9199/02.png&quot;
        srcset=&quot;/static/506ab21e78ba1226b817519678aaf51e/8ff5a/02.png 240w,
/static/506ab21e78ba1226b817519678aaf51e/e85cb/02.png 480w,
/static/506ab21e78ba1226b817519678aaf51e/d9199/02.png 960w,
/static/506ab21e78ba1226b817519678aaf51e/07a9c/02.png 1440w,
/static/506ab21e78ba1226b817519678aaf51e/da994/02.png 1487w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;With &lt;code class=&quot;language-text&quot;&gt;import &apos;rxjs&apos;&lt;/code&gt;, you do not need to import each operator manually. However, &lt;strong&gt;I highly recommend NOT to use this code since it imports the entire RxJS library, and we do not need all RxJS functionalities and operators in our projects.&lt;/strong&gt; You can see the big different below.&lt;/p&gt;
&lt;p&gt;Then I replace the &lt;code class=&quot;language-text&quot;&gt;import &apos;rxjs&apos;&lt;/code&gt; with the block of code below to import only the neccessary that I need.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// RxJS&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Subscription &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/Subscription&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Observable &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/Observable&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Subject &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/Subject&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; BehaviorSubject &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/BehaviorSubject&apos;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Observable class extensions&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/observable/of&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/observable/fromEvent&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/observable/empty&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/observable/forkJoin&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Observable operators&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/map&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/concat&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/delay&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/do&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/catch&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/switchMap&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/merge&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/mergeMap&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/filter&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/debounceTime&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/add/operator/distinctUntilChanged&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can the size was reduced significantly. The size of RxJS now will be just &lt;strong&gt;60kb&lt;/strong&gt;, which is less than &lt;strong&gt;10%&lt;/strong&gt; of my vendor bundle, comparing to &lt;strong&gt;26%&lt;/strong&gt; before.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 575px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3dfabbbb966f5805b486ae2ed06b47fc/59415/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABuElEQVQozz2R227aQBRFhzRpXAMGcQukSSOiVIEQB9/GdxvbQEyhlYLat/7/d6wWQ/KwtaWjmaV99hGdbIfqZ7TiFVf5hv6iZJBtKl0YIeJJImYuYvbuJ53mtWcP8WgxTEvs338R37d/uF2U3BVbZrs9bS+nFxRoMkVMnePHyl3EVB69gp/mB01sLl4CmjJDNP0VDT+iHWV0kyW9tOBM92j7Swbxmk5QcJWsEbrkk+Fy9iI/4P1oxW3+g/Fqh2JGFVgIPeTctmlKA02aaL6NavmMQp+uF1RraW6G4rvUF5LLwKVhWTQMswI+lL/o+Dli4lA7VCH0mNqzpB+YXMcGbddEmUtuYp+OPHalmglfQhdtKVEil7Zt0bXnDJNXJts3emFxrOYgoUeoiaS+dFFzl4vQoRVk3JdvXGeb6lHdXlQgbe2iJqejTCSjRclkt2f6c0/zvfMD8NJyaIWykiodNCvka5Qz8FLOnySaGdPwJK1E0vAliu7weeYwDAvG2YZv6bo6ykfCuulwE1rcRQajwKJjejwmMeMgQpk69KyEbigZriTdSNI3LQbGnIf0FWe3Z2THx3T/k/8Du4LuEMlq/VYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;angular tips avoid import rxjs&quot;
        title=&quot;angular tips avoid import rxjs&quot;
        src=&quot;/static/3dfabbbb966f5805b486ae2ed06b47fc/59415/03.png&quot;
        srcset=&quot;/static/3dfabbbb966f5805b486ae2ed06b47fc/8ff5a/03.png 240w,
/static/3dfabbbb966f5805b486ae2ed06b47fc/e85cb/03.png 480w,
/static/3dfabbbb966f5805b486ae2ed06b47fc/59415/03.png 575w&quot;
        sizes=&quot;(max-width: 575px) 100vw, 575px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e65f2fbe25448a70b0709c474e9393b6/497e1/04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 54.58333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABIklEQVQoz41Ri27DIAzk/7+zalYpEBLAPMNNpnXFukyapYtjbB9nrIgiSinIOV+iloJlWXC73f6sEcQYodxxoLWK8zwHWmvovcM5h8fjgeNwIKIBzkldrc+e3Vpovb5FKaNX1FrAxkQMtrlJbM5Lji8RESklKP5I8dw420wkXkhmG4Q8ytz0H3wSih+E/JBXhFcKr0b+RTgr/CS5Oheb31ZsLCWEgJO31xqC9zDGjPVzzCp4tJQitNaw1o5tMplzB4zWg4SVcd++71AkI7PknOFDQKn1qaL3Qcix9x4UCfW1VanlS+Xf+wBF1qLnjDOl4cEKXjGjEqEEQuOLOfc667lMceRHRTwOKLd8IW4WZMwPxDc2hHUdoOlsrht+27Df7/gGu+Ve+Z2SjIwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;angular tips avoid import rxjs&quot;
        title=&quot;angular tips avoid import rxjs&quot;
        src=&quot;/static/e65f2fbe25448a70b0709c474e9393b6/d9199/04.png&quot;
        srcset=&quot;/static/e65f2fbe25448a70b0709c474e9393b6/8ff5a/04.png 240w,
/static/e65f2fbe25448a70b0709c474e9393b6/e85cb/04.png 480w,
/static/e65f2fbe25448a70b0709c474e9393b6/d9199/04.png 960w,
/static/e65f2fbe25448a70b0709c474e9393b6/07a9c/04.png 1440w,
/static/e65f2fbe25448a70b0709c474e9393b6/497e1/04.png 1891w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I hope that the Angular team will update the document soon, at least to notice developer not to import the whole RxJS library. Because they might not be using it at all.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Analyze webpack bundle with source-map-explorer. Optimize moment.js]]></title><description><![CDATA[I found that source-map-explorer tool shows an easy-to-understand-and-explore visualization to help you debug where all the code is coming from.]]></description><link>https://trungvose.comanalyze-webpack-bundle/</link><guid isPermaLink="false">https://trungvose.comanalyze-webpack-bundle/</guid><pubDate>Wed, 31 Jan 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Webpack is a module bundler which means that it bundles together all of your JavaScript files to one or multiple files commonly named bundle.js. If you want to know the difference between all the terminology such as module bundle, module loader or &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;, see my &lt;a href=&quot;https://stackoverflow.com/a/39825582/3375906&quot;&gt;answer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently our bundle size suddenly getting so much bigger than before. Although &lt;code class=&quot;language-text&quot;&gt;webpack&lt;/code&gt; provide the information of how your bundle was composed of. I found that &lt;a href=&quot;https://github.com/danvk/source-map-explorer&quot;&gt;source-map-explorer&lt;/a&gt; tool shows an easy-to-understand-and-explore visualization to help you debug where all the code is coming from.&lt;/p&gt;
&lt;p&gt;Take note of the image below. The size of &lt;code class=&quot;language-text&quot;&gt;client-api.js&lt;/code&gt; file before was just &lt;strong&gt;100kb&lt;/strong&gt; only.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 510px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/753cf1b6ed87107535049376cdfe20bd/0abdd/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABk0lEQVQoz02QbXPSUBCFb1t5a5GoILQjIm0tQiHNy00u4SaBBMoQQBmqozN+8v//iseJjaMfnjm7O7Pn7Kw48xfU5xvq4SPGYoOhV9SjNQ29omzNEPc+Yqz+6V+K/mQyRdw5OE8/CX78QvTSjP4y4+HwjcnnJ7pJRj57n245MwPE0CtM/IL/6vuCoeRtuKab7BAvVcpVsmO4/8q5jBHjKRU35mQSULY1FScslhRilJObPNd5YNnSf7TmRpyaU0TDsWn6LrerFMPTVB5cbtKYjg4xVMKH5Y4LP6I89agGihdScWFLDMeioRbYx++M9kfusgOlhxmi5dt0ZyaGa1K1XGq2S9MbU3csxEBymqdPQ85Tj9pcUdKK157NdTSmJkNuNgfepRli5D1f3wscbrVFL3BpWi5dL8BMIvoqoDqUtCxNR2kul5LLuU9L+3Qsh740absh3u7IZLXlSsZU89e0Fj7NWNEMfV55io/zDYNky3W0pjxweGOFtJWmnUjahWF1qCh98mjLGHvzhfHjHjc70LA1vwFSMdIX00UbkwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;analyze webpack bundle source-map-explorer&quot;
        title=&quot;analyze webpack bundle source-map-explorer&quot;
        src=&quot;/static/753cf1b6ed87107535049376cdfe20bd/0abdd/01.png&quot;
        srcset=&quot;/static/753cf1b6ed87107535049376cdfe20bd/8ff5a/01.png 240w,
/static/753cf1b6ed87107535049376cdfe20bd/e85cb/01.png 480w,
/static/753cf1b6ed87107535049376cdfe20bd/0abdd/01.png 510w&quot;
        sizes=&quot;(max-width: 510px) 100vw, 510px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I have made some changes recently and the size climbed up to 600+ kb. The reason is that another module was included in it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 512px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1e6fb4aa91fa1e5cc6520773b17d05ab/01e7c/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.66666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAABl0lEQVQoz02R227aUBBFTwATILSpChhHTaOUkHL1wbdzbAw24epQCkRV//9XVgV+6cPSHs1Io5m9Re/wl2/plu7uSP/XB739me/LdzqbA63ZGjHSiKHKdfS/qrx/oe9jzndYaYZ42p5pxhuetyf6hz/U/ISinFL1U8Q45NaJKYxDxEAhBjrXoc7rkaYko+u87MTcjDWiKGNq0qGhQ36sNlSdkMpE8fyWcq/mWMkOK9lSdBVlpTECjSEVn1yXqhvR23/QzU50syO3kylC2DPaoeQxtrn3JRXHx5ABVmhTlj6iF1yvraaa+kpRW2oqns9LMqTuazrZmU52zK0YBAhzEvEa2PxUkpdgQsv2ePIkPT2iaXvUhwrLnWPGiodFQDtRmI7HJB7y4Crk9jfj1Z7HIKF2WWotItqph5kGNCOfryqik6wZrHc0/RlG36cVLGgkitYy504qPo89vjgR9vsJOzvi7E/ULy8X1AwjUpRCTVFpbhzNnXqjMdtQcuJrioYzo6Q15ShHXMzvKwpyipnuaM031PXimvg/jn3Rf0o5RnwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;analyze webpack bundle source-map-explorer&quot;
        title=&quot;analyze webpack bundle source-map-explorer&quot;
        src=&quot;/static/1e6fb4aa91fa1e5cc6520773b17d05ab/01e7c/02.png&quot;
        srcset=&quot;/static/1e6fb4aa91fa1e5cc6520773b17d05ab/8ff5a/02.png 240w,
/static/1e6fb4aa91fa1e5cc6520773b17d05ab/e85cb/02.png 480w,
/static/1e6fb4aa91fa1e5cc6520773b17d05ab/01e7c/02.png 512w&quot;
        sizes=&quot;(max-width: 512px) 100vw, 512px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That’s when source-map-explorer come and shine. It will not be installed into your dependency, but globally via npm.&lt;/p&gt;
&lt;p&gt;Install:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; source-map-explorer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use: the source map file needs to be present.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;source-map-explorer bundle.min.js
source-map-explorer bundle.min.js bundle.min.js.map&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After running the command, this is how my client-api.js bundle looks like. You can see most of the space was taken by &lt;code class=&quot;language-text&quot;&gt;moment.js&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;intl-tel-input.js&lt;/code&gt;. My actual code is located inside the module which only takes 40kb. So the question now is how to optimize the bundle?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/064182f320ff92179985892eb4316691/29114/03.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABUElEQVQoz41Si3KDIBD0//8xJlEeCh4aiaAR3RZMWmPbmeLs3N3OsS5w2TRNeDweCdM4wRgDe79/ceM4gogw9j1Gazf0PVqlYMm8cb63yNpGI651XRFCQNfd4L3fuGXBGmMI6JVCSwSjNUhr1FKiqWuQUtBVhUYpdFwgi5ueitivdVcv8wxT1aC2TW4bIhAZkDHQWm8gQiclstfmv/ASbIRAVVfgnEMIkWJdVSmXUqJkDMT4/wUV52BcQCmFsiyTkBASRVmiVipFww+ChzO/CVaXK86nHKwowK5Fys95juJ6gWAM+ekEw9i34P5hlue97vnHMCA4h/kJd7thshaL9wjepzg5h2yeZ7wQR2gYhjQqR24OYXvx6Dx9SFjWNSHmzntk0dEe0d0ezjm0bZtm8kfvobbWboJ7l7GOcxhdxTw6jPVvPfFnW8+IwTncPwU/AFsAB4P2pCouAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;analyze webpack bundle source-map-explorer&quot;
        title=&quot;analyze webpack bundle source-map-explorer&quot;
        src=&quot;/static/064182f320ff92179985892eb4316691/d9199/03.png&quot;
        srcset=&quot;/static/064182f320ff92179985892eb4316691/8ff5a/03.png 240w,
/static/064182f320ff92179985892eb4316691/e85cb/03.png 480w,
/static/064182f320ff92179985892eb4316691/d9199/03.png 960w,
/static/064182f320ff92179985892eb4316691/07a9c/03.png 1440w,
/static/064182f320ff92179985892eb4316691/29114/03.png 1920w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One of the obvious parts we can take out is the moment.js locale, which accounts for &lt;strong&gt;167kb&lt;/strong&gt; and we are not using it at all. It happened because when you write &lt;code class=&quot;language-text&quot;&gt;var moment = require(&apos;moment&apos;)&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;import * as moment from &apos;moment&apos;&lt;/code&gt; in your code and pack with webpack, the bundle will include all locale files.&lt;/p&gt;
&lt;p&gt;To remove all locale files, I will use the IgnorePlugin. Refer to &lt;a href=&quot;https://stackoverflow.com/q/25384360/3375906&quot;&gt;this question&lt;/a&gt; for more options.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Ignore all locale files of moment.js&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;webpack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IgnorePlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^\.\/locale$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;moment$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And you can still load some locales in your code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; moment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;moment&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;moment/locale/ja&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

moment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ja&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I rebuild and re-analyze the bundle, you can see how all the locale was gone. We have just saved 100kb, quite a lot right?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a462bc24f040fc5ec910122bafe264de/9e818/04.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.41666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABBElEQVQoz61TiY6FIAzk/z/TbHhqlKJc4jWb8lKDZrNHsk1qYTqtg0VlLWFZFqzripxz8W3b4JyD1i+ktCCEULzmpJQKb55nDMNQcoyp10eDvCSwnecJseM4CqnGauOmUiMc5it+1ImaIPbMsXND4UlkTD3f9JML998a/lrhX+3WsMIUT4YHsO/75UzkbxtjLJO8chVP6sRFtfrqOmitQWRveMlZizzPWJ1DJAK1bYm8X50v62vKz+Owiqdx0Xe2xXhXKJGPKark6KycFWTvkbkmhPfahwsLxkB579/kGMvfME0TGOPIe8aJCNZakDHQTQPT95hGg7HvMbQdOq0xdj1oGPEJM1pfb1iz18MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;analyze webpack bundle source-map-explorer&quot;
        title=&quot;analyze webpack bundle source-map-explorer&quot;
        src=&quot;/static/a462bc24f040fc5ec910122bafe264de/d9199/04.png&quot;
        srcset=&quot;/static/a462bc24f040fc5ec910122bafe264de/8ff5a/04.png 240w,
/static/a462bc24f040fc5ec910122bafe264de/e85cb/04.png 480w,
/static/a462bc24f040fc5ec910122bafe264de/d9199/04.png 960w,
/static/a462bc24f040fc5ec910122bafe264de/07a9c/04.png 1440w,
/static/a462bc24f040fc5ec910122bafe264de/9e818/04.png 1913w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I hope you guys find it helpful to apply to your project. Cheer.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Uncaught TypeError: Cannot read property 'name' of undefined]]></title><description><![CDATA[It is a very common error when working with object and array to get a TypeError: Cannot read property 'name' of undefined. This happens when we try to access a property of a value that is undefined or null in JavaScript]]></description><link>https://trungvose.comuncaught-type-error/</link><guid isPermaLink="false">https://trungvose.comuncaught-type-error/</guid><pubDate>Mon, 22 Jan 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It is a very common error when working with object and array to get a &lt;code class=&quot;language-text&quot;&gt;TypeError: Cannot read property &apos;name&apos; of undefined&lt;/code&gt;. This happens when we try to access a property of a value that is undefined or null in JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8bdc45099cea73dcfafa773ee71157c4/ad997/01.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABNElEQVQoz6WRXWvrMBiD8/9/3ODQrWvoclKS1Y5j+7X7lWaJ42e0bNBdnjOBQAjdSCqmOCL+ynC+sCwLOUPO+Z94Q0ozb68rio954jfIj3pZKK7+TJAR0w1YOxJl5Hq5ICJEEbxz9F3HIQSCCOIc1hhc3/MxjnBrdW92r0YxVjXpz4rp+ZX0vGZerclVRdpsWOqaafvGVJYsZUlqW9J6zVyWTE3LUlWw3UJdQwjkECgmE4jbd9zLDvu04bprONV/kd0OUYpBN0S1J2pN0BrXNPi2ZbSadDrdOR+PpPOZPAwUrvco4+hc5N0ILhzwMdIqxV5rou/wtsNYR9cbtDHslULEE48HfAj0zuFFmOaZwlqLvgW8I90OenjuPnR+ED/8r5e/9vvWhTEGpRTee+Y5/Qz/Bz8BMUtowO3TqCwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;uncaught type error&quot;
        title=&quot;uncaught type error&quot;
        src=&quot;/static/8bdc45099cea73dcfafa773ee71157c4/d9199/01.png&quot;
        srcset=&quot;/static/8bdc45099cea73dcfafa773ee71157c4/8ff5a/01.png 240w,
/static/8bdc45099cea73dcfafa773ee71157c4/e85cb/01.png 480w,
/static/8bdc45099cea73dcfafa773ee71157c4/d9199/01.png 960w,
/static/8bdc45099cea73dcfafa773ee71157c4/ad997/01.png 1012w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; movie &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Interstellar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;director&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Christopher Nolan&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;born&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;July 30, 1970&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;music&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hans Zimmer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;movie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;director&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Christopher Nolan&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;movie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;music&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//undefined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;movie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cast&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Uncaught TypeError: Cannot read property &apos;0&apos; of undefined&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Uncaught TypeError: Cannot read property &apos;deepProp&apos; of undefined&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;workarounds&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#workarounds&quot; aria-label=&quot;workarounds permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Workarounds&lt;/h3&gt;
&lt;p&gt;There are some ways to avoid this kind of errors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. The simplest approach is using the logical &lt;code class=&quot;language-text&quot;&gt;AND operator &amp;amp;&amp;amp;&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Uncaught TypeError: Cannot read property &apos;deepProp&apos; of undefined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//undefined&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This works because the &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; operators actually return one of the value of the specified operands if these operators are used with non-Boolean values. The rule is described as. If we had &lt;code class=&quot;language-text&quot;&gt;expr1 &amp;amp;&amp;amp; expr2&lt;/code&gt;, it will&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Returns expr1 if it can be converted to false; otherwise, returns expr2.&lt;/li&gt;
&lt;li&gt;Thus, when used with Boolean values, &amp;#x26;&amp;#x26; returns true if both operands are true; otherwise, returns false.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See more on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators&quot;&gt;Expressions and operators&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;obj.prop1 &amp;amp;&amp;amp; obj.prop1.deepProp&lt;/code&gt; returns &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; because &lt;code class=&quot;language-text&quot;&gt;obj.prop1&lt;/code&gt; is undefined which will be converted/coerced to &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;. The reason behind how JavaScript uses type coercion in Boolean contexts. You can read more, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Truthy&quot;&gt;Truthy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I often use the &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; to check If the property is actually existed before going down further.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//good&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//error prone&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//Uncaught TypeError: Cannot read property &apos;deepProp&apos; of undefined&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It works well for small chains of properties, but getting ugly very soon If we are going too deep into a property.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;prop1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; neededProperty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryVeryDeepProp
&lt;span class=&quot;token comment&quot;&gt;//Uncaught TypeError: Cannot read property &apos;veryVeryDeepProp&apos; of undefined&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; deepProperty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryDeepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryVeryDeepProp
&lt;span class=&quot;token comment&quot;&gt;//Uncaught TypeError: Cannot read property &apos;veryDeepProp&apos; of undefined&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//Same error as above because prop1 is an object which will be coerced to true so that&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//obj.prop1.deepProp.veryVeryDeepProp operand will be evaluated and throw and error&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; safeProperty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
  obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryDeepProp &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryVeryDeepProp
&lt;span class=&quot;token comment&quot;&gt;//undefined&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//This is a very safe check but the code will be messy.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;2. Using try/catch blocks.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because some things can go wrong…&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; deepProperty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1 &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryDeepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryVeryDeepProp
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// do something else&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But placing many try/catch blocks throughout your code just to access properties is neither practical nor readable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Helper function&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We will write a simple helper function that does nothing else but calling a provided function in a try/catch block. With the support of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions&quot;&gt;ES6 Arrow function&lt;/a&gt;, we can call the helper function in just a one-line callback.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSafe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// use it like this&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; deepProperty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSafe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryDeepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryVeryDeepProp
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 675px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1f4c11c3a858f6b31c47811cf5e90168/23296/02.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA90lEQVQoz52SSW6EMBBFff8jZZVljoEQc+MRPIHNj8oJKC1l0c2Xvmx5eOWqMnM6YDEOznmklJHze04pgdTVFb4+P8Ai97AqQMoIa+N14GUdRxm2GLAaBRaXANNpCKGx7/vvmeNtn2J+9RCdgOgF9GBKGndg55ylTK86cIBAGXf09ML5wTE8JIaZg6sZUko4Z+GshRACWutrVEphWRas6wpjTFkLIfzUcNuKmeASdd2i70b0XY+qqtC2LcZxLHBr7QWjC9Q0MtWbTCUiUQAKxkLwaJoGw9AXyDRNBXJGfiXdp5QJRiCK9neDdKdBjOrhvb/1Vf4DfgPyiQ//b3DxHwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;uncaught type error&quot;
        title=&quot;uncaught type error&quot;
        src=&quot;/static/1f4c11c3a858f6b31c47811cf5e90168/23296/02.png&quot;
        srcset=&quot;/static/1f4c11c3a858f6b31c47811cf5e90168/8ff5a/02.png 240w,
/static/1f4c11c3a858f6b31c47811cf5e90168/e85cb/02.png 480w,
/static/1f4c11c3a858f6b31c47811cf5e90168/23296/02.png 675w&quot;
        sizes=&quot;(max-width: 675px) 100vw, 675px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That way, you will either get the property value or undefined. That works because JavaScript only evaluates the content of the inner callback (or closure) when it is actually executed. So it will only be evaluated inside of the try/catch block and not yet in the code calling the helper function. This version down below will &lt;strong&gt;never work&lt;/strong&gt; because the property argument will be evaluated when we pass it into the function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSafeNaive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; property
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; deepProperty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSafeNaive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryDeepProp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;veryVeryDeepProp
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//error from here, so that cannot go inside the try catch block&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;elvis-operator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#elvis-operator&quot; aria-label=&quot;elvis operator permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Elvis Operator&lt;/h3&gt;
&lt;p&gt;Some programming languages also support the so called elvis operator &lt;code class=&quot;language-text&quot;&gt;?.&lt;/code&gt; that is yet another approach to the same problem. Applying it to the dirty example from above would like this &lt;code class=&quot;language-text&quot;&gt;obj?.prop1?.deepProp?.veryDeepProp?.veryVeryDeepProp&lt;/code&gt;. Essentially, it makes the compiler stop accessing more nested properties as soon as one of them is null (or undefined, or whatever null-type(s) that language uses).&lt;/p&gt;
&lt;p&gt;Currently, there is no elvis operator in neither JavaScript nor TypeScript.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In Angular, there is the support elvis operator to protect against a view render failure. They call it &lt;a href=&quot;https://angular.io/guide/template-syntax#expression-operators&quot;&gt;safe navigation operator&lt;/a&gt;. Take the example below&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;The current person name is &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;nullObject&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since it is trying to access &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; property of a null value, &lt;strong&gt;the whole view disappears&lt;/strong&gt; and you can see the error inside the browser console. It works perfectly with long property paths such as &lt;code class=&quot;language-text&quot;&gt;a?.b?.c?.d&lt;/code&gt;. So I recommend you to use it everytime you need to access a property inside a template.&lt;/p&gt;
&lt;h3 id=&quot;typescript-37---optional-chaining&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#typescript-37---optional-chaining&quot; aria-label=&quot;typescript 37   optional chaining permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TypeScript 3.7 - Optional Chaining&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining&quot;&gt;Detail Release Note&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After a long wait, finally TypeScript has brought this feature to live. It basically is the elvis operator above.&lt;/p&gt;
&lt;p&gt;At its core, optional chaining lets us write code where TypeScript can immediately stop running some expressions if we run into a null or undefined. The star of the show in optional chaining is the new ?. operator for optional property accesses. When we write code like&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; foo&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;this is a way of saying that when foo is defined, foo.bar.baz() will be computed; but when foo is null or undefined, stop what we’re doing and just return undefined.”&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that if &lt;code class=&quot;language-text&quot;&gt;bar&lt;/code&gt; is null or undefined, our code will still hit an error accessing baz. Likewise, if &lt;code class=&quot;language-text&quot;&gt;baz&lt;/code&gt; is null or undefined, we’ll hit an error at the call site. ?. only checks for whether the value on the left of it is null or undefined - not any of the subsequent properties.&lt;/p&gt;
&lt;p&gt;You might find yourself using ?. to replace a lot of code that performs repetitive nullish checks using the &amp;#x26;&amp;#x26; operator.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bar &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After-ish&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;bar&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Keep in mind that &lt;code class=&quot;language-text&quot;&gt;?.&lt;/code&gt; acts differently than those &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; operations since &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt; will act specially on &lt;code class=&quot;language-text&quot;&gt;“falsy”&lt;/code&gt; values (e.g. the empty string, 0, NaN, and, well, false), but this is an intentional feature of the construct. It doesn’t short-circuit on valid data like 0 or empty strings.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The combination of debounce and throttle]]></title><description><![CDATA[Debounce and throttle are two techniques to control how many times we allow a function to be executed over time]]></description><link>https://trungvose.comdebounce-throttle-combination/</link><guid isPermaLink="false">https://trungvose.comdebounce-throttle-combination/</guid><pubDate>Thu, 18 Jan 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Debounce and throttle are two techniques to control how many times we allow a function to be executed over time.&lt;/p&gt;
&lt;p&gt;The real world example could be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wait until the user stops resizing the window&lt;/li&gt;
&lt;li&gt;Don’t fire an ajax event until the user stops typing&lt;/li&gt;
&lt;li&gt;Measure the scroll position of the page and respond at most every 50ms&lt;/li&gt;
&lt;li&gt;Ensure good performance as you drag elements around in an app&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Throttling enforces a maximum number of times a function can be called over time. As in “execute this function at most once every 100 milliseconds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Say under normal circumstances you would call this function 1,000 times over 10 seconds. If you throttle it to only once per 100 milliseconds, it would only execute that function at most 100 times&lt;/p&gt;
&lt;p&gt;(10s * 1,000) = 10,000ms
10,000ms / 100ms throttling = 100 maximum calls&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called. As in “execute this function only if 100 milliseconds have passed without it being called.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Perhaps a function is called 1,000 times in a quick burst, dispersed over 3 seconds, then stops being called. If you have debounced it at 100 milliseconds, the function will only fire once, at 3.1 seconds, once the burst is over. Each time the function is called during the burst it resets the debouncing timer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Quoting from &lt;a href=&quot;https://css-tricks.com/the-difference-between-throttling-and-debouncing/&quot;&gt;The Difference Between Throttling and Debouncing&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;iframe height=&apos;265&apos; scrolling=&apos;no&apos; title=&apos;Combination of throttling and debounce &apos; src=&apos;//codepen.io/trungvose/embed/dJEJYy/?height=265&amp;theme-id=0&amp;default-tab=js,result&amp;embed-version=2&apos; frameborder=&apos;no&apos; allowtransparency=&apos;true&apos; allowfullscreen=&apos;true&apos; style=&apos;width: 100%;&apos;&gt;See the Pen &lt;a href=&apos;https://codepen.io/trungvose/pen/dJEJYy/&apos;&gt;Combination of throttling and debounce &lt;/a&gt; by Vo Tuan Trung (&lt;a href=&apos;https://codepen.io/trungvose&apos;&gt;@trungk18&lt;/a&gt;) on &lt;a href=&apos;https://codepen.io&apos;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;p&gt;But what I want to archive current is kind of the combination between debounce and throttle.&lt;/p&gt;
&lt;p&gt;Basically, our system has a long process that will fire a lot of update to the client. When I received the server command on the client,I will need additional HTTP requests back to server to get some information. So that It will hit the server back as long as the server sending me update.&lt;/p&gt;
&lt;p&gt;I was using debounce to prevent sending too many requests back to the server If the time between two commands is within 1s. But there is one use case where the server constantly sending the update to the client in, e.g 10 minutes. Meaning the client will trigger the getItemCount at 10m 1s.&lt;/p&gt;
&lt;p&gt;So that I came up with the idea of combo, which is the combination of both debounce and throttle.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Trigger the UI update immediately when receiving the first event sending from the server.&lt;/li&gt;
&lt;li&gt;If there are more event to come, don’t run the next event unless it is less than 1 second from the last event.&lt;/li&gt;
&lt;li&gt;If the server keeps sending the event. Run the condition (2) above and do the update every 60 seconds.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Find a sequence to produce a number by adding 5 or multiplying by 3]]></title><description><![CDATA[Just read an interesting problem this morning. It seems not to be difficult]]></description><link>https://trungvose.comfunny-recursion/</link><guid isPermaLink="false">https://trungvose.comfunny-recursion/</guid><pubDate>Fri, 12 Jan 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just read an interesting problem this morning. It seems not to be difficult.&lt;/p&gt;
&lt;p&gt;Starting from the number 1 and repeatedly either adding 5 or multiply by 3, an infinite amount of new number can be produced. How would you write a function that, given a number, tries to find a sequence of such additions and multiplications that produce that number? For example, the number of 13 could be reached by first multiplying by 3, and then adding 5 twice. Whereas the number 15 cannot be reached at all.&lt;/p&gt;
&lt;p&gt;What came first to my mind is very obvious, recursive function. It doesn’t find the shortest sequence of operation. It is satisfied when it finds a sequence.&lt;/p&gt;
&lt;iframe height=&apos;265&apos; scrolling=&apos;no&apos; title=&apos;Find sequence to produce a number by adding 5 or multiplying by 3&apos; src=&apos;//codepen.io/trungvose/embed/baKamv/?height=265&amp;theme-id=0&amp;default-tab=js,result&amp;embed-version=2&apos; frameborder=&apos;no&apos; allowtransparency=&apos;true&apos; allowfullscreen=&apos;true&apos; style=&apos;width: 100%;&apos;&gt;See the Pen &lt;a href=&apos;https://codepen.io/trungvose/pen/baKamv/&apos;&gt;Find sequence to produce a number by adding 5 or multiplying by 3&lt;/a&gt; by Vo Tuan Trung (&lt;a href=&apos;https://codepen.io/trungvose&apos;&gt;@trungk18&lt;/a&gt;) on &lt;a href=&apos;https://codepen.io&apos;&gt;CodePen&lt;/a&gt;.&lt;/iframe&gt;
&lt;p&gt;The inner function find does the actual recursing. It takes two arguments - the current number and a string that records how we reached this number. And return either a string or null.&lt;/p&gt;
&lt;p&gt;It will perform one of three actions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the current number is the target number, the current history is a way to reach that target, so it is simply returned.&lt;/li&gt;
&lt;li&gt;If the current number is greater than the target number, simple return null because we will never reach the target from here.&lt;/li&gt;
&lt;li&gt;If the current number is below the target number, the function tries both possible paths that start from the current number, by calling itself twice, once for each of the allowed next steps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My approach is starting from 1 and try to reach the target number. But the problem could be resolved by starting from the target number and try to reach 1. It is going to be a lot easier. You can see more from &lt;a href=&quot;https://codereview.stackexchange.com/questions/55838/find-sequence-by-adding-5-or-multiplying-by-3&quot;&gt;here&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[10k StackOverFlow]]></title><link>https://trungvose.com10k-stackoverflow/</link><guid isPermaLink="false">https://trungvose.com10k-stackoverflow/</guid><pubDate>Sun, 07 Jan 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been almost two years since I started answering on StackOverFlow. It got 10k today :)&lt;/p&gt;
&lt;p&gt;See my profile.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/users/3375906/trungvose&quot;&gt;&lt;img src=&quot;https://stackoverflow.com/users/flair/3375906.png&quot; width=&quot;208&quot; height=&quot;58&quot; alt=&quot;profile for trungvose at Stack Overflow, Q&amp;amp;A for professional and enthusiast programmers&quot; title=&quot;profile for trungvose at Stack Overflow, Q&amp;amp;A for professional and enthusiast programmers&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is my &lt;a href=&quot;https://stackoverflow.com/a/39825582/3375906&quot;&gt;most popular answer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/65e259d9aa5851959a7471d643429fff/daadd/stack-10000k.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAB2HAAAdhwGP5fFlAAABXUlEQVQoz3XR226cMBAGYN7/AVuliiq1y8IavIDtsT1Hu2JRk4so39XczOHXDJAiQE6nXErNpSJSqVX0RETMOk+33+9vOSdIMD3W++yOkI6QhoQGZBGtlBJjCvEcg4j9ZT/C3T0gF6hYkXaoLtQY166Ppsew+M2t3vktAYgI8UlVr+YmHIkXUOBzR5VG0p/uV8cfjcbBr6dlcUjUv2Askw/v835bjz/LPvojV0Z4dHxrPA+r99M0pZTMTESuqBcRZcJUK5Am1FiVxFrvXFyvPxtPg/f+CKf8AgCllP91Jiyx0lE1kSUyUrPWuW6d/zZ5Dszcv9eEtoLjwVuWZxZARTFEFFEzG6bTvG1bKRUAiD+Tt967SURds+xFn0USamu9vk5j5uE2jg/nQghEhIhEpCLMcsk57zGVc1MTa2rNzD4eOdynaVkWRPyI+pkccghh2/cQE4nx61QzU7Or+R8ktUDvNFA2eAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;StackOverFlow&quot;
        title=&quot;StackOverFlow&quot;
        src=&quot;/static/65e259d9aa5851959a7471d643429fff/d9199/stack-10000k.png&quot;
        srcset=&quot;/static/65e259d9aa5851959a7471d643429fff/8ff5a/stack-10000k.png 240w,
/static/65e259d9aa5851959a7471d643429fff/e85cb/stack-10000k.png 480w,
/static/65e259d9aa5851959a7471d643429fff/d9199/stack-10000k.png 960w,
/static/65e259d9aa5851959a7471d643429fff/07a9c/stack-10000k.png 1440w,
/static/65e259d9aa5851959a7471d643429fff/29114/stack-10000k.png 1920w,
/static/65e259d9aa5851959a7471d643429fff/daadd/stack-10000k.png 2169w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Integrate Angular 2+ and Webpack in an ASP.NET MVC 5 application]]></title><description><![CDATA[I will summarize the steps needed to integrate Angular 2 and Webpack build process in an MVC 5 application]]></description><link>https://trungvose.comasp.net-mvc-5-angular-webpack/</link><guid isPermaLink="false">https://trungvose.comasp.net-mvc-5-angular-webpack/</guid><pubDate>Thu, 21 Dec 2017 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ASP.NET MVC 5 is widely used web development framework, it’s stable, matured and most importantly it is used in production on large scale. In this post, I will summarize the steps needed to integrate Angular 2 and Webpack build process in an MVC 5 application.&lt;/p&gt;
&lt;p&gt;We will follow these steps to archive it.&lt;/p&gt;
&lt;p&gt;Source code &lt;a href=&quot;https://github.com/trungvose/mvc5-angular-webpack&quot;&gt;https://github.com/trungvose/mvc5-angular-webpack&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Noted that the source code is using html-webpack-plugin as described below.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Structure the project.&lt;/li&gt;
&lt;li&gt;Configure &lt;code class=&quot;language-text&quot;&gt;webpack&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Configure npm script.&lt;/li&gt;
&lt;li&gt;Configure build.bat.&lt;/li&gt;
&lt;li&gt;Reference compiled JavaScript file in the Razor view (.cshtml).&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 850px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4f335e3989a03b69d33d7454db380bb6/ae694/build-process.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64.58333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACI0lEQVQoz42Pa0/TUBjHn7GNXdoN3IWCOBggu89tbU/bnXParle2MSfOGDQkJoQQv4AZUQQU+Ia+9I1fQhMcqGkR4gtIPPnll/9Jnv+5wJev3yaTy9//sa5+ef55dfnj6mIyufx+MYGd8dHbk7Pdw9O947P9T+f7J+eeb7nZ7h2f7ow/7B5+fj1+/+Lg4M3B0fa7jwBVGcoClEVYr8NK4S+rN2Gl6OXVAqwVYa0EuSIsluCRT64EGeo+1Nx51c2ZvQV1I42tOWrPKganOlliJ1BnRjISSJ+RDNZzx8tyJynp000MDDISSIvxNCnpSUlnkcYgLcqTJNKvJyItEmniaR8/E88tEm60gev056jFUZtTnXnVWdDclH9thlgxnkZbhBU1RvROj/GUEVVGpKyoJSQ9gTQI8Z1gAwcb5JZQk4Z5NdSkgZoCFXQ3Zd/xthNBelw22LbJKEagLkNNDtwAFQmq95Pf2Mrbg7wzeIDtNLaCdRnKktfxa8G6MlVX7i1HhXaWtFmhnVFInKcJpIUlFBak6JMWp9pLZm+6gb0X3llmdMxqAquQx85muIEjAp6x5DiRA1W0bPWXrd71XKAqeb+oyoGaX6tJXkgRJ43NymCUdwaF7rPKYJQze0tWn6NuzujOq3oMC1MtEYoilAQo/WsBSsPt2vBlc/Qq7w7Xulvr/edL9tNld7ho9rmOs2C7K93NWdXMUjerumnqpIgPtrOq+wdFdr6UMbqgZgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;module 1&quot;
        title=&quot;module 1&quot;
        src=&quot;/static/4f335e3989a03b69d33d7454db380bb6/ae694/build-process.png&quot;
        srcset=&quot;/static/4f335e3989a03b69d33d7454db380bb6/8ff5a/build-process.png 240w,
/static/4f335e3989a03b69d33d7454db380bb6/e85cb/build-process.png 480w,
/static/4f335e3989a03b69d33d7454db380bb6/ae694/build-process.png 850w&quot;
        sizes=&quot;(max-width: 850px) 100vw, 850px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Structure the project&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We separated our project into two folders: &lt;strong&gt;Client and Server&lt;/strong&gt;. Those will be located in &lt;strong&gt;mvc5-angular-webpack&lt;/strong&gt; folder and this folder will be committed to the repository&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;mvc5-angular-webpack/
├── Server/
│   ├── WebApplication/
│   │     ├── Controllers/
│   │     ├── Scripts/
│   │     ├── Web.config
│   │     ├── Many more folder and file...
│   │
│   └── Web-Core.sln
│
├── Client/
    ├── modules
    │     ├── angularModule-1/
    │     │      ├── main.ts
    │     │      ├── app.modules.ts
    │     │      ├── app.component.ts
    │     │      ├── Many more file...
    │     │
    │     ├── angularModule-2/
    │     │      ├── main.ts
    │     │      ├── app.modules.ts
    │     │      ├── app.component.ts
    │     │      ├── Many more file...
    │     ├── polyfill.ts
    │     ├── vendor.ts
    │
    └── build.bat
    └── npm-shrinkwrap.json
    └── package.json
    └── tsconfig.json
    └── tslint.json
    └── webpack.config.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;Server&lt;/strong&gt; folder, we added the MVC solution named &lt;code class=&quot;language-text&quot;&gt;Web-Core.sln&lt;/code&gt; and all the common library project is written in C#.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Client&lt;/strong&gt; folder only contains front-end related stuff. To build the front project, simply call the &lt;code class=&quot;language-text&quot;&gt;build.bat&lt;/code&gt;. I will talk about this file content later. Inside modules folder, our project will create each subfolder for each module.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our website has some module still using server-side rendering with pure Razor. And there is some module written in client-side code with AngularJS and Angular.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Configure &lt;code class=&quot;language-text&quot;&gt;webpack&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Assume that you configured all the &lt;code class=&quot;language-text&quot;&gt;typescript&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; already. Let see what I have inside &lt;a href=&quot;https://github.com/trungvose/mvc5-angular-webpack/blob/master/Client/webpack.config.js&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;webpack.config.js&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;path&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; UglifyJsPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;uglifyjs-webpack-plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entryPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;modules&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; corePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../Server/WebApplication/Scripts/ng2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; module1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entryPath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/angularModule-1&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; module2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entryPath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/angularModule-2&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;envOptions&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  envOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; envOptions &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;polyfills&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entryPath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/polyfill.ts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;vendors&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entryPath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/vendor.ts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;module1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;module1&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/main.ts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;module2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;module2&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/main.ts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; corePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name].js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;sourceMapFilename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name].js.map&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;extensions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.ts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.ts$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;loaders&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;awesome-typescript-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;angular2-template-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.html$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;raw-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;\.css$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;raw-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;devtool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;source-map&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;webpack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NoEmitOnErrorsPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;webpack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;optimize&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CommonsChunkPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;vendors&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;polyfills&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;envOptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MODE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;prod&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UglifyJsPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So basically it will try to resolve the directory in an upper level and put all the compiled files to &lt;strong&gt;Scripts/ng2&lt;/strong&gt; inside &lt;strong&gt;Server&lt;/strong&gt; folder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Configure npm script&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After configured &lt;code class=&quot;language-text&quot;&gt;webpack&lt;/code&gt;, we will basically add some more script to run during the build process. Add the following code to &lt;a href=&quot;https://github.com/trungvose/mvc5-angular-webpack/blob/master/Client/package.json&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;&lt;/a&gt; file&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token string-property property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;tsc&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tsc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;tsc:w&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tsc -w&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack-dev-server --https --open&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;watch&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --config webpack.config.js --watch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --config webpack.config.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;build:html&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --config webpack-html-plugin.config.js --env.MODE=prod&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;build:prod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --config webpack.config.js --env.MODE=prod&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;4. Configure &lt;code class=&quot;language-text&quot;&gt;build.bat&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At the beginning of Angular 2 integration, we have created an empty web application project for front-end purpose and added this project as a dependency of our &lt;code class=&quot;language-text&quot;&gt;WebApplication&lt;/code&gt;. But our backend team later complained about how slow the front-end process take. Because they don’t need the front-end project to build every time the &lt;code class=&quot;language-text&quot;&gt;WebApplication&lt;/code&gt; is being built.&lt;/p&gt;
&lt;p&gt;The idea of the &lt;code class=&quot;language-text&quot;&gt;build.bat&lt;/code&gt; file is to manually run it to get the latest version of front-end code on their machine. Not every single time they run the project.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;call npm install --production  --loglevel verbose
echo &quot;Build Angular projects&quot;
npm run build:prod&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt; is to continue because some of the commands abort the command line. Refer &lt;a href=&quot;https://stackoverflow.com/questions/4673714/how-to-execute-several-batch-commands-in-sequence&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The script here is very simple. First, we run the npm install to restore all the necessary dependency. Then call &lt;code class=&quot;language-text&quot;&gt;build:prod&lt;/code&gt; as we defined on &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; before. Webpack will take care of bundle our Typescript code into three big JavaScript files as &lt;code class=&quot;language-text&quot;&gt;vendors.js&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;polyfills.js&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;module1.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our team used Jenkins for deployment, so our dev ops just need to include to run the &lt;code class=&quot;language-text&quot;&gt;build.bat&lt;/code&gt; and we are all set. If you want to run it everytime your project was built, you can set it inside the pre-build or post-build event.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://i.stack.imgur.com/GZyCe.png&quot;&gt;&lt;img src=&quot;https://i.stack.imgur.com/GZyCe.png&quot; alt=&quot;pre-build and post-build&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;http://www.aaronstannard.com/visualstudio-pre-build-tasks/&quot;&gt;Image source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Reference compiled JavaScript file in the Razor view (.cshtml).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Normally we will return only one view in an area as below. The &lt;code class=&quot;language-text&quot;&gt;my-angular-app&lt;/code&gt; is what we defined in &lt;code class=&quot;language-text&quot;&gt;app.component.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Index.cshtml&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;~/Scripts/ng2/polyfills.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;~/Scripts/ng2/vendors.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;~/Scripts/ng2/module1.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;my-angular-app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Loading...
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;my-angular-app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;HomeController.cs&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Module1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That is a bit drawback If we deploy to production. Because after the compiled JavaScript was updated, the browser sometimes still keep the old version of the file because of caching. We should have a mechanism to provide a unique name for the files after deployment. There are 3 options for us to do so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;i. &lt;a href=&quot;https://github.com/jantimon/html-webpack-plugin&quot;&gt;html-webpack-plugin&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If we work with pure front-end project, &lt;code class=&quot;language-text&quot;&gt;webpack&lt;/code&gt; provides &lt;a href=&quot;https://github.com/jantimon/html-webpack-plugin&quot;&gt;html-webpack-plugin&lt;/a&gt; to take care of it as below. Technically, It will automatically inject the JavaScript file into our view with the unique id.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/mvc5-angular-webpack/blob/master/Client/webpack-html-plugin.config.js&quot;&gt;webpack-html-webpack-plugin.config.js&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; HtmlWebpackPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;html-webpack-plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; viewPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    __dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;../Server/WebApplication/Views/Home&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token literal-property property&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;polyfills&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entryPath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/polyfill.ts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;vendors&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entryPath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/vendor.ts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;module1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;module1&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/main.ts&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token literal-property property&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; corePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[name].[hash].js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;sourceMapFilename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[name].[hash].js.map&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HtmlWebpackPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; viewPath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/loader.cshtml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; viewPath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Module1.cshtml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And in the same folder with the designated view, we created a cshtml file named loader.cshtml&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trungvose/mvc5-angular-webpack/blob/master/Server/WebApplication/Views/Home/loader.cshtml&quot;&gt;loader.cshtml&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;% for (var chunk in htmlWebpackPlugin.files.chunks) { %&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&amp;lt;%= htmlWebpackPlugin.files.chunks[chunk].entry %&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&amp;lt;% } %&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;my-angular-app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Loading...
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;my-angular-app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And run the &lt;code class=&quot;language-text&quot;&gt;npm run build:html&lt;/code&gt; as defined on &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;. If it runs successfully, the result will look like that.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 697px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/af6aacfbad3c0c1319f29a3ae9885c04/7422e/build-process-html.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 28.333333333333332%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA+0lEQVQY03WOzU7DMBCE8xyxd9eOiRMndn6c2gVCiUJaKLTiQk+8/4OgFlT1wqe5zEijmaQqjM41EZVFQUg61+ZMJYTo2i4MA/1PQraQrpS2UF2tnJFSiguIKKVUSuENRHRrk+fl5fVw3L1/PM3zw2bDkRhjiMgYMJayNEVABISzLgljAPBXXvtmXK+6Ki8yuKPUu9LVhgjatnSutbYBAhRCKdQ680PsvVdZ9ttPmq2pH/PVwcZjEz/b8cubXtta7/f3WuvaWTn3MPXWovfl6fQdQ+DXZR4tbHr+Fvk+8l1IlwFCPa7HaVoEkcikWDxtPWNAJMMqVMZcb/8AB8c32k1EA4MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;build html plugin&quot;
        title=&quot;build html plugin&quot;
        src=&quot;/static/af6aacfbad3c0c1319f29a3ae9885c04/7422e/build-process-html.png&quot;
        srcset=&quot;/static/af6aacfbad3c0c1319f29a3ae9885c04/8ff5a/build-process-html.png 240w,
/static/af6aacfbad3c0c1319f29a3ae9885c04/e85cb/build-process-html.png 480w,
/static/af6aacfbad3c0c1319f29a3ae9885c04/7422e/build-process-html.png 697w&quot;
        sizes=&quot;(max-width: 697px) 100vw, 697px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 548px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cc96908602be36afb07b910837980b59/a58fe/network-html-plugin.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABg0lEQVQoz22Sy47kIAxF8/8/WMvqVMCQ8A4YyIOkFawuaaQ5CzboyscXhpTwvu/2L/d9r+vqnAsh7PseY9Rat9au6zrP03vfWjuOY2Cf98/PCABSyhhjrTXnXEoRQiillmUJIYzj+Hq9QgiImFLy3rPOIIFxDq5jraUz5+yci51a6zzPjLFSyrZttaOUmud5UDMsyzPBex863vvjOFJKpRREPM/TWiulpHWu67rvO4SgtR4En6buIDpSSgDw3mutySLGKIQYxzGllHMm86mHBgETxZRS+o91Xa21ZJFzFkJ8Pp+cc621dOZ5FkIMRknTV107JL9tW0qJyvuvdozRGDNwNnIOAGCt3fd9+0MpRdopJQB4v9/UQu4AAOe8hwGoW7pAxFqr1tp775xLKZE2IlIYEQHg0VaL0NqEEMjnK/bVbq0ZY4QQpE0/ylr7aD9tT8xa833DUsq+7zSZCiNtMqLhUspHW3DGOHfOffehH2bMo2OMiTFKKRljiEjOiKiUklL+AriEpNMi/bfkAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;network html plugin&quot;
        title=&quot;network html plugin&quot;
        src=&quot;/static/cc96908602be36afb07b910837980b59/a58fe/network-html-plugin.png&quot;
        srcset=&quot;/static/cc96908602be36afb07b910837980b59/8ff5a/network-html-plugin.png 240w,
/static/cc96908602be36afb07b910837980b59/e85cb/network-html-plugin.png 480w,
/static/cc96908602be36afb07b910837980b59/a58fe/network-html-plugin.png 548w&quot;
        sizes=&quot;(max-width: 548px) 100vw, 548px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;@{ ViewBag.Title = &quot;Module 1&quot;; }
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;../../Scripts/ng2/vendors.1470de344a0f2260b700.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;../../Scripts/ng2/vendors.1470de344a0f2260b700.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;../../Scripts/ng2/module1.1470de344a0f2260b700.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;my-angular-app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Loading....&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;my-angular-app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Noted that If we have multiple module as the github source code, it might load all the compiled js file, which is &lt;code class=&quot;language-text&quot;&gt;module1&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;module2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is the reason why we decided to use the second below approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ii. Defined your own JavaScript version&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In our ASP.NET MVC project is a bit different because we used more than one Angular app. So that we defined a version in the class and append it at the end of the file when loading the JS. By doing it, we will make sure the latest will be loaded into the browser. But I think it is out of this question context so I will not go further. Basically, It is gonna look like below code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&quot;@string.Format(&quot;{0}?v={1}&quot;, &quot;~/Scripts/ng2/polyfills.js&quot;, VersionHelper.CurrentVersion())&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;script src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;@string.Format(&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;v&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Scripts&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ng2&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;vendors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&quot;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; VersionHelper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CurrentVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&amp;lt;script src=&quot;@string.Format(&quot;{0}?v={1}&quot;, &quot;~/Scripts/ng2/module1.js&quot;, VersionHelper.CurrentVersion())&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When serving it on browser, it will look like&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;~/Scripts/ng2/polyfills.js?v=1.1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;~/Scripts/ng2/vendors.js?v=1.1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;~/Scripts/ng2/module1.js?v=1.1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;iii. &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/mvc/overview/performance/bundling-and-minification&quot;&gt;ASP.NET Bundling&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At Zyllem, our team didn’t use it because of our JavaScript files is configure inside the model and render it later to the view.&lt;/p&gt;
&lt;p&gt;You can just open &lt;code class=&quot;language-text&quot;&gt;App_Start\BundleConfig.cs&lt;/code&gt; in your project and config a bundle. Let say the name is &lt;code class=&quot;language-text&quot;&gt;module1&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;csharp&quot;&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;bundles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;ScriptBundle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;~/bundles/module1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;~/Scripts/ng2/polyfills.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;~/Scripts/ng2/vendors.js&quot;&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;~/Scripts/ng2/module1.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And render inside the view by doing it.&lt;/p&gt;
&lt;p&gt;Index.cshtml&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;@Scripts.Render(&quot;~/bundles/module1&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So that when serving on the browser, it will have the unique token at the end and it is different If you make any changes in the script bundle.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/bundles/module1?v=2PbUw0z_gh_Ocp_Vz13rdmmBSG6utLJAXm2lThxYAow1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If everything works fine, you will see the below screenshots.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d31734c98ab767f20ec76e84f0257f13/7798c/screen-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABCUlEQVQoz5XQS2/CMAwA4PwtEJo4lF++w057nBvXTdqOOCkJSWjUdEgwUWCaNGnavoNfsi9mxWZTFMXDbL1eL5fLxd1qtVr8itV1LS7qKyGElHKeiLfXZ0T8ar8XUsqu61gIIcY4DMOQUprD4XBIs5enx17TOI7D3WUlpRhjiDGlxKy1IQQpBfASEQGgbduyLAEAqwoRS86xqgBACME5B4C+751zIQRmjGmkRNFA3WkipZQxRilFRMYYrTUR3YLWSilNZK29HSulOOcoJHa92Tnv/X6/99f0w2XsvXPOXo+JqGlk+75ttqbdmt3O/pH3nuWcP47HnMcpjzmPfzdNE5t/PJxPp/P/fQKa6coYUGAWKAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;module 1&quot;
        title=&quot;module 1&quot;
        src=&quot;/static/d31734c98ab767f20ec76e84f0257f13/d9199/screen-1.png&quot;
        srcset=&quot;/static/d31734c98ab767f20ec76e84f0257f13/8ff5a/screen-1.png 240w,
/static/d31734c98ab767f20ec76e84f0257f13/e85cb/screen-1.png 480w,
/static/d31734c98ab767f20ec76e84f0257f13/d9199/screen-1.png 960w,
/static/d31734c98ab767f20ec76e84f0257f13/7798c/screen-1.png 1183w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4741c99e46a7be4e829eb5f8188ae1c9/b5cea/screen-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABC0lEQVQoz53PTUvDQBAG4P2xLeq5uru5tLQepBpN8SCFVg9BqQfxD3iKmFNKa0jRYq0Ggx7EfOwmu4Fks5Xix0UQzctcHmZgZgBECGOMMIYQQYQghLXa+h8LCCFEUSxKBYQkfguJH0YBoTRhNGEBoSGJ/Yh4zy/uk/fqhxFNAkJjxr+7HwStA32t1dlQu5W6un10utU7WW1qitavNtT24aDdH1Qbu4rWq9R3NP1ss6uvNPeUzpL7x+fAHDsXpnU1cgzLtqfz69t7Y2ib44kxXNKezj9p2c7dw+hmdvnFyewRSM4KzhZSlvk59bzcdXNChJTF7/kxAEgcJ2nKOBdC/HtzlmUp4yIXJc5+B42kzYws0ntPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;module 2&quot;
        title=&quot;module 2&quot;
        src=&quot;/static/4741c99e46a7be4e829eb5f8188ae1c9/d9199/screen-2.png&quot;
        srcset=&quot;/static/4741c99e46a7be4e829eb5f8188ae1c9/8ff5a/screen-2.png 240w,
/static/4741c99e46a7be4e829eb5f8188ae1c9/e85cb/screen-2.png 480w,
/static/4741c99e46a7be4e829eb5f8188ae1c9/d9199/screen-2.png 960w,
/static/4741c99e46a7be4e829eb5f8188ae1c9/b5cea/screen-2.png 1140w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;for-development&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#for-development&quot; aria-label=&quot;for development permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;For development&lt;/h2&gt;
&lt;p&gt;You don’t have to waste time to run &lt;code class=&quot;language-text&quot;&gt;npm run build&lt;/code&gt; everytime to get the latest changes while doing development.&lt;/p&gt;
&lt;p&gt;Just run &lt;code class=&quot;language-text&quot;&gt;npm run watch&lt;/code&gt;. Webpack will monitor and automatically rebuild the bundle file in case we change the code. But we still have to refresh the browser to get it reflected. For &lt;a href=&quot;https://webpack.js.org/concepts/hot-module-replacement/&quot;&gt;hot module replacement&lt;/a&gt;, I will write about it in the upcoming post.&lt;/p&gt;
&lt;p&gt;Let me know in comments if you face any issues running applications.&lt;/p&gt;
&lt;hr&gt;</content:encoded></item><item><title><![CDATA[Spinning button onSubmit form with jQuery and ASP.NET MVC]]></title><description><![CDATA[At Zyllem, we are leveraging ASP.NET MVC 5 to build our product. So basically, almost 80% of our code base and UI stuff is doing as server side. 20% of the code is the client-side app written in JavaScript in AngularJS, and also TypeScript in Angular 2/4.]]></description><link>https://trungvose.comspinning-button-on-submit-jquery/</link><guid isPermaLink="false">https://trungvose.comspinning-button-on-submit-jquery/</guid><pubDate>Mon, 11 Dec 2017 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At Zyllem, we are leveraging ASP.NET MVC 5 to build our product. So basically, almost 80% of our code base and UI stuff is doing as server side. 20% of the code is the client-side app written in JavaScript in AngularJS, and also TypeScript in Angular 2/4.&lt;/p&gt;
&lt;iframe height=&apos;265&apos; scrolling=&apos;no&apos; title=&apos;Spinning button onSubmit form with jQuery&apos; src=&apos;//codepen.io/trungvose/embed/MrWrNw/?height=265&amp;theme-id=0&amp;default-tab=js,result&amp;embed-version=2&apos; frameborder=&apos;no&apos; allowtransparency=&apos;true&apos; allowfullscreen=&apos;true&apos; style=&apos;width: 100%;&apos;&gt;See the Pen &lt;a href=&apos;https://codepen.io/trungvose/pen/MrWrNw/&apos;&gt;Spinning button onSubmit form with jQuery&lt;/a&gt; by Vo Tuan Trung (&lt;a href=&apos;https://codepen.io/trungvose&apos;&gt;@trungk18&lt;/a&gt;) on &lt;a href=&apos;https://codepen.io&apos;&gt;CodePen&lt;/a&gt;.&lt;/iframe&gt;
&lt;hr&gt;
&lt;p&gt;There is a traditional UX issue is when we submit the form, sometimes the server takes a long time to respond to the client so that the page stays the same. Most of the time, what we do is to try to click the submit button again, and again. In few last sprint, my boss and I introduced the spinner button.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We add an attribute &lt;code class=&quot;language-text&quot;&gt;data-spinning-button&lt;/code&gt; to the button with type submit in every form.&lt;/li&gt;
&lt;li&gt;In jQuery, we find the parents of the button that match a form and try to validate the form.&lt;/li&gt;
&lt;li&gt;If the form is valid, change the button content to be a spinner with a Fontawesome icon.&lt;/li&gt;
&lt;li&gt;Because it is the server side code, so every time we finished proceeding on the server the whole view will be rendered again. It is same If there is any error, so we don’t have to worry about If the error happens.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The purpose is to inform user that the system is proceeding their request and also prevent them from submitting the form multiple times by disabling the button.&lt;/p&gt;
&lt;p&gt;The sample code in MVC looks like.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;@using (var f = Html.Bootstrap().Begin(new Form().LabelWidthMd(4))) {
@Html.Bootstrap().TextBoxFor(x =&gt;
x.UserName).Placeholder(&quot;Email&quot;).ShowValidationMessage(true)
@Html.Bootstrap().PasswordFor(x =&gt;
x.Password).Placeholder(&quot;Password&quot;).ShowValidationMessage(true)

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn btn-primary btn-block&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-spinning-button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Log in
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; zyllemMain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;processSubmitLoader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;button[data-spinning-button]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; $&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; formId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;spinning-button&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; $form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; formId &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;#&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; formId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;form&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//form.valid() will be applicable If you are using jQuery validate https://jqueryvalidation.org/&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//asp.net mvc used it by default with jQuery Unobtrusive Validation&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          $&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;i class=&apos;fa fa-circle-o-notch fa-spin&apos;&gt;&amp;lt;/i&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;disabled&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          $form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;initSpinnerButton&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; processSubmitLoader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  zyllemMain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initSpinnerButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[JavaScript naming convention]]></title><description><![CDATA[There are only two hard things in Computer Science: cache invalidation and naming things]]></description><link>https://trungvose.comjavascript-naming-convention/</link><guid isPermaLink="false">https://trungvose.comjavascript-naming-convention/</guid><pubDate>Thu, 30 Nov 2017 16:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;“There are only two hard things in Computer Science: cache invalidation and naming things” - Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;tldr&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tldr&quot; aria-label=&quot;tldr permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TL;DR&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Variable name is always camel case and should begin with a noun;&lt;/li&gt;
&lt;li&gt;Functions name should begin with a verb.&lt;/li&gt;
&lt;li&gt;Make the variable name as short as possible.&lt;/li&gt;
&lt;li&gt;A single-character variable name such as &lt;code class=&quot;language-text&quot;&gt;i&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;j&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt; are typically reserved for use in loops.&lt;/li&gt;
&lt;li&gt;The meaningless name should be avoided as well, such as &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;bar&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To define a constant variable, remember to use all uppercase letters with underscores separating words.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I have been working with C# and JavaScript for the past few years and came to realize that each language seems to have its own set of naming conventions. I’d like to share with you some naming conventions for JavaScript that I personally prefer.&lt;/p&gt;
&lt;p&gt;Most of the code you write involves variables and functions, so determining the naming for those is very important to a comprehensive code. JavaScript’s core, ECMAScript, is written using a convention called &lt;strong&gt;&lt;em&gt;camel case&lt;/em&gt;&lt;/strong&gt;. Basically, its name begins with a lower case letter and each sub-sequent word begins with an uppercase letter. E.g&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; theName
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; anotherVariable&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Camel-case is also the most JavaScript developers name variable and function.&lt;/p&gt;
&lt;p&gt;You might hear of &lt;a href=&quot;https://en.wikipedia.org/wiki/Hungarian_notation&quot;&gt;Hungarian notations&lt;/a&gt; was popular for JavaScript a few years back. It involved prepending a variable type identifier at the beginning of a name.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; strName &lt;span class=&quot;token comment&quot;&gt;//string&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; bBusy &lt;span class=&quot;token comment&quot;&gt;//boolean&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;variable-and-functions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#variable-and-functions&quot; aria-label=&quot;variable and functions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Variable and Functions&lt;/h2&gt;
&lt;p&gt;Variable name is always camel case and should begin with a noun to differentiate variables from functions, which normally should begin with a verb.&lt;/p&gt;
&lt;p&gt;But for boolean variable, I will make an exception, usually I named them as &lt;code class=&quot;language-text&quot;&gt;isOnlyActive: boolean&lt;/code&gt;, which is similar to the function name that return a boolean.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//good&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; myName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Trung&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isOnlyActive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//bad: confused with function&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; getCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//good&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; myName
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//bad: confused with variable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; myName
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Generally speaking, you should try to make the variable name as short as possible. Try to make the variable name indicate the data type of its value. For example, &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;length&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;size&lt;/code&gt; suggest a data type is a number. &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt; sounds like as string. A single-character variable name such as &lt;code class=&quot;language-text&quot;&gt;i&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;j&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt; are typically reserved for use in loops.&lt;/p&gt;
&lt;p&gt;The meaningless name should be avoided as well, such as &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;bar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For functions and methods name, the first word should always be a verb, and there are some common conventions used as described in the table below.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verb&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;can/ has/ is&lt;/td&gt;
&lt;td&gt;Function returns a boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;get&lt;/td&gt;
&lt;td&gt;Function returns a non-boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set&lt;/td&gt;
&lt;td&gt;Function to save a value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;constants&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#constants&quot; aria-label=&quot;constants permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Constants&lt;/h2&gt;
&lt;p&gt;To differentiate normal variables (which to have changes value) and constants (which initialized to a value and never change), I often use the all uppercase letters with underscores separating words.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAX_ITEM&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://www.zyllem.com/discover-zyllem&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example&quot; aria-label=&quot;example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example&lt;/h2&gt;
&lt;p&gt;This is applicable for TS as well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; _newString&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; newVar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; user$&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Observable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;User&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; isUserTokenValid&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; canPerformAction&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; ids&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_MINUTE_IN_MILLISECONDS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;filterUsers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; User&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;update&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#update&quot; aria-label=&quot;update permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Update&lt;/h2&gt;
&lt;p&gt;Recently, I have been reading this awesome guide. It is all about how to write clean Javascript code. You guys can check it on the below link.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ryanmcdermott/clean-code-javascript&quot;&gt;https://github.com/ryanmcdermott/clean-code-javascript&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Shrinking Navigation Bar When Scrolling Down - Bootstrap 3 Navigation & jQuery]]></title><description><![CDATA[In this tutorial, I will show you how to create an animated fixed navigation that will resize on scroll and when you scroll down the page a bit, the header resizes smaller, and gets back bigger when you scroll back to the top with just simple CSS3 animation and jQuery]]></description><link>https://trungvose.comresizing-header-on-scrolling/</link><guid isPermaLink="false">https://trungvose.comresizing-header-on-scrolling/</guid><pubDate>Sun, 26 Nov 2017 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this tutorial, I will show you how to create an animated fixed navigation that will resize on scroll and when you scroll down the page a bit, the header resizes smaller, and gets back bigger when you scroll back to the top with just simple CSS3 animation and jQuery.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/trungvose/Resizing-Header-On-Scroll&quot;&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://trungvose.github.io/resizing-header-on-scroll/&quot;&gt;Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Codepen, please view in full screen.&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe height=&apos;265&apos; scrolling=&apos;no&apos; title=&apos;Shrinking Navigation Bar When Scrolling Down&apos; src=&apos;//codepen.io/trungvose/embed/opazdV/?height=265&amp;theme-id=0&amp;default-tab=result&amp;embed-version=2&apos; frameborder=&apos;no&apos; allowtransparency=&apos;true&apos; allowfullscreen=&apos;true&apos; style=&apos;width: 100%;&apos;&gt;See the Pen &lt;a href=&apos;https://codepen.io/trungvose/pen/opazdV/&apos;&gt;Shrinking Navigation Bar When Scrolling Down&lt;/a&gt; by Vo Tuan Trung (&lt;a href=&apos;https://codepen.io/trungvose&apos;&gt;@trungk18&lt;/a&gt;) on &lt;a href=&apos;https://codepen.io&apos;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;h3 id=&quot;sticky-navigation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sticky-navigation&quot; aria-label=&quot;sticky navigation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sticky Navigation&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;To make a sticky nav you need to add the class navbar-fixed-top to your nav&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Official documentation: &lt;a href=&quot;http://getbootstrap.com/components/#navbar-fixed-top&quot;&gt;Bootstrap navbar fixed top&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let’s now take a look at our HTML markup. The markup consists of a header with a logo and navigation, some sections with color difference only.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Navigation --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;nav&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;navbar navbar-default navbar-fixed-top&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Brand and toggle get grouped for better mobile display --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;navbar-header page-scroll&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;navbar-toggle&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;data-toggle&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;collapse&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;data-target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#top-nav&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;sr-only&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Toggle navigation&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon-bar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon-bar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon-bar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;brand&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://trungk18.github.io/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;trungk18.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;img-responsive&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;trungk18&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Collect the nav links, forms, and other content for toggling --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;collapse navbar-collapse&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;top-nav&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;nav navbar-nav navbar-right&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page-scroll&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#portfolio&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Portfolio&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page-scroll&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#about&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;About&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page-scroll&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#contact&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Contact&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- /.navbar-collapse --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- /.container-fluid --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;nav&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Content Section --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;portfolio&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;about&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;contact&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I am using navbar-default from bootstrap with navbar-fixed-top to make it stick to the top of webpage. The HTML is fairly straightforward.&lt;/p&gt;
&lt;h3 id=&quot;now-take-a-look-at-css&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#now-take-a-look-at-css&quot; aria-label=&quot;now take a look at css permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Now take a look at CSS&lt;/h3&gt;
&lt;p&gt;Class .navbar-default was set the padding top and down for 30px each, and the transition is based on padding. When the navbar gets resized on scroll, I shrink the value to 10px by adding child class navbar-shrink.
Finally, I added some example media queries so that our animated resizing navigation will work only in medium devices desktops &lt;a href=&quot;http://getbootstrap.com/css/#grid&quot;&gt;Bootstrap standard grid&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; screen &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 992px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.navbar-default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30px 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; padding 0.3s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.navbar-default.navbar-shrink&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.navbar-default a&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #4d4d4d&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Segoe UI&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tahoma&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Geneva&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Verdana&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; uppercase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 42px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 700&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.navbar-default a.brand &gt; img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 70px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.navbar-default a.active&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #2dbccb&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.content &gt; section&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;#portfolio&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #2dbccb&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;#about&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #eb7e7f&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;#contact&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #415c71&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;jquery-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jquery-code&quot; aria-label=&quot;jquery code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jQuery code&lt;/h3&gt;
&lt;p&gt;To make it happen, I am going to use jQuery to add and remove the class &lt;em&gt;navbar-shrink&lt;/em&gt; when we scroll a certain amount (in this situation is 50px). Adding and removing this class will animate the navbar.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scroll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//Method 1: Using addClass and removeClass&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//if ($(document).scrollTop() &gt; 50) {&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//    $(&apos;.navbar-default&apos;).addClass(&apos;navbar-shrink&apos;);&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//} else {&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//    $(&apos;.navbar-default&apos;).removeClass(&apos;navbar-shrink&apos;);&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;//Method 2: Using toggleClass&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.navbar-default&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggleClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;navbar-shrink&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollTop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can use &lt;a href=&quot;http://api.jquery.com/toggleclass/&quot;&gt;toggleClass&lt;/a&gt; with 2 parameter.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;first parameter&lt;/em&gt; will be the target element.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;second parameter&lt;/em&gt; for determining whether the class should be added or removed. If this parameter’s value is true, then the class is added; if false, the class is removed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Nowadays it is not really hard to search for component we need to use in our front-end project, but the most important thing is you can understand how it’s worked and you can easily implement this animated resizing header into your next project.&lt;/p&gt;
&lt;p&gt;Thanks for checking it out and view the demo, feel free to fork and fix the source code then pull back to me.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 in my daily life]]></title><description><![CDATA[ES2015/ES6 has been around for more than two years with all the exciting feature and syntax. After working with TypeScript and Angular 2 in a project for more than a year, there are the features that I often apply in my code.]]></description><link>https://trungvose.comes6-in-my-daily-life/</link><guid isPermaLink="false">https://trungvose.comes6-in-my-daily-life/</guid><pubDate>Wed, 22 Nov 2017 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ES2015/ES6 has been around for more than two years with all the exciting feature and syntax. After working with TypeScript and Angular 2 in a project for more than a year, there are the features that I often apply in my code.&lt;/p&gt;
&lt;h3 id=&quot;template-literals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#template-literals&quot; aria-label=&quot;template literals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&quot;&gt;Template literals&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It provides the syntax for constructing strings. Basically, it helps us to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write multiple lines of string&lt;/li&gt;
&lt;li&gt;Interpolate JavaScript into your string using &lt;code class=&quot;language-text&quot;&gt;${}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//basic literal string creation&lt;/span&gt;
&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;This is a pretty little template string.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Multiline strings&lt;/span&gt;
&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;in ES5 this is
 not legal.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//construct string without the need to concat&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/api/v1/users/${userId}&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;post&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;application/json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&apos;Content-Type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;application/json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;destructuring&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#destructuring&quot; aria-label=&quot;destructuring permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&quot;&gt;Destructuring&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It provides a better way to unpack value from arrays, or properties from an object into a variable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rest
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 10&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 20&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;default-parameter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#default-parameter&quot; aria-label=&quot;default parameter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters&quot;&gt;Default Parameter&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It allows formal parameters to be initialized with default values if no value or undefined is passed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//es5&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//function multiply(a, b) {&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//  b = (typeof b !== &apos;undefined&apos;) ?  b : 1;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//  return a * b;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//es6&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;multiply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; b
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;multiply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 10&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;multiply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;multiply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;-spread-operator-and-rest-parameters&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-spread-operator-and-rest-parameters&quot; aria-label=&quot; spread operator and rest parameters permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator&quot;&gt;… (spread operator and rest parameters)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These provide a convenient way to shove any unnamed arguments passed to your function into an array, or do the inverse by calling a function with arguments extracted from an array.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; arr1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; arr2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//append all items from arr2 onto arr1&lt;/span&gt;
arr1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//with spread syntax this becomes:&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; arr1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; arr2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
arr1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr2&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;my-useful-tips-collection-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#my-useful-tips-collection-&quot; aria-label=&quot;my useful tips collection  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;My useful tips collection :)&lt;/h3&gt;
&lt;h4 id=&quot;1-swap-variable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-swap-variable&quot; aria-label=&quot;1 swap variable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Swap variable&lt;/h4&gt;
&lt;p&gt;Using array destructive syntax&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;world&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hello&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// -&gt; hello&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// -&gt; world&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;2-concatmerge-array&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-concatmerge-array&quot; aria-label=&quot;2 concatmerge array permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Concat/merge array&lt;/h4&gt;
&lt;p&gt;I often use spread operator to extract array value instead of concat function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arr1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arr2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arr3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//old es5 way&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arr3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//another old es5 way&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arr2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arr3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//new es6 way&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr3&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;3-default-variable-with-&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-default-variable-with-&quot; aria-label=&quot;3 default variable with  permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Default variable with ||&lt;/h4&gt;
&lt;p&gt;You will find it sometimes very very helpful.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//old es5 way&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//new es6 way&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;//todo&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;4-time-testing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-time-testing&quot; aria-label=&quot;4 time testing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Time testing&lt;/h4&gt;
&lt;p&gt;Ever wonder what’s faster: Looping with an i++ or looping with an i— ? Just use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/console#Timers&quot;&gt;console’s timing&lt;/a&gt; method to test&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;c&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;d&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;e&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;f&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;g&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;h&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;i&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;testing with i++&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;timeEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;testing with i++&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//testing with i++: 0.010009765625ms&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;testing with i--&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;timeEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;testing with i--&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//testing with i--: 0.0107421875ms&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;to-learn-es6&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#to-learn-es6&quot; aria-label=&quot;to learn es6 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;To learn ES6&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://babeljs.io/learn-es2015/&quot;&gt;https://babeljs.io/learn-es2015/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://exploringjs.com/es6/&quot;&gt;http://exploringjs.com/es6/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting with the release of ES2015, the language has continued to evolve at a rapid pace. We saw there is the tons of feature of &lt;a href=&quot;http://exploringjs.com/es2016-es2017/&quot;&gt;ES2017&lt;/a&gt; on the way, such as &lt;code class=&quot;language-text&quot;&gt;async/await&lt;/code&gt; function. So hurry up and digest the ES 2015 before it become outdated.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[My new blog]]></title><description><![CDATA[Hello people 👋]]></description><link>https://trungvose.commy-new-blog/</link><guid isPermaLink="false">https://trungvose.commy-new-blog/</guid><pubDate>Sun, 19 Nov 2017 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Friends, I’m updating my portfolio with some recent activity and preparing a small blog to share my experience in web development. In the meanwhile, just visit my Linkedin or drop me a message on &lt;a href=&quot;mailto:trungk18@gmail.com&quot;&gt;trungk18 et gmail.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See you around!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JavaScript: Understanding the Weird Parts Notes]]></title><description><![CDATA[Notes from a Udemy course JavaScript: Understanding the Weird Parts]]></description><link>https://trungvose.comjavascript-weird-parts/</link><guid isPermaLink="false">https://trungvose.comjavascript-weird-parts/</guid><pubDate>Fri, 10 Apr 2015 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;These are my notes from a Udemy course - &lt;a href=&quot;https://www.udemy.com/course/understand-javascript/&quot;&gt;JavaScript: Understanding the Weird Parts&lt;/a&gt; that I took about a years ago. Back then, I had a shallow understanding of how JavaScript worked. I only knew the basics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;var x&lt;/code&gt; to create a variable&lt;/li&gt;
&lt;li&gt;How to write a function. Most beginners did not even know the difference between &lt;code class=&quot;language-text&quot;&gt;function declaration&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;function expression&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;How to invoke a callback for asynchronous operations such as HTTP calls.&lt;/li&gt;
&lt;li&gt;How to work with objects - &lt;code class=&quot;language-text&quot;&gt;{}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;How to work with arrays and some basic manipulation - &lt;code class=&quot;language-text&quot;&gt;[]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Some jQuery for DOM manipulation. Working with jQuery taught me useful patterns like the module pattern and the revealing module pattern, which led me to learn about Immediately Invoked Function Expressions (IIFE).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With just this basic knowledge and no deep understanding of JavaScript, I managed to build a &lt;a href=&quot;https://www.statecourts.gov.sg/cws/Pages/mHearing.aspx&quot;&gt;mobile app&lt;/a&gt; on my own. It was live for years with many active users. Not my best work, but it gave me a real chance to work with &lt;code class=&quot;language-text&quot;&gt;AngularJS&lt;/code&gt; on something more meaningful than a simple Todo app.&lt;/p&gt;
&lt;p&gt;Over time, I felt the need to learn JavaScript properly. That is why I started this course. It was genuinely helpful in shaping how I think about the language, and the foundation still holds up today.&lt;/p&gt;
&lt;h2 id=&quot;syntax-parser&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#syntax-parser&quot; aria-label=&quot;syntax parser permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Syntax Parser&lt;/h2&gt;
&lt;p&gt;A program that reads your code and determines what it does and if its grammar is valid.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The compiler translates the entire program before it is run.&lt;/li&gt;
&lt;li&gt;The interpreters translate a line at a time while the program is being run.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;lexical-environment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lexical-environment&quot; aria-label=&quot;lexical environment permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lexical Environment&lt;/h2&gt;
&lt;p&gt;In programming languages, where you write something matters. The location of code and what surrounds it affects how it behaves.&lt;/p&gt;
&lt;h2 id=&quot;execution-context&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#execution-context&quot; aria-label=&quot;execution context permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Execution Context&lt;/h2&gt;
&lt;p&gt;A wrapper to help manage the code that is running. There are lots of lexical environments. Which one is currently running is managed via execution contexts.&lt;/p&gt;
&lt;h2 id=&quot;single-threaded-synchronous-execution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#single-threaded-synchronous-execution&quot; aria-label=&quot;single threaded synchronous execution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Single Threaded, Synchronous Execution&lt;/h2&gt;
&lt;p&gt;One command at a time, in order. Under the hood of the browser, maybe not.&lt;/p&gt;
&lt;h2 id=&quot;synchronous-vs-asynchronous&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#synchronous-vs-asynchronous&quot; aria-label=&quot;synchronous vs asynchronous permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Synchronous vs Asynchronous&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Synchronous&lt;/th&gt;
&lt;th&gt;One at a time and in order that it appeared&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Asynchronous&lt;/td&gt;
&lt;td&gt;More than one at a time.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;invocation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#invocation&quot; aria-label=&quot;invocation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Invocation&lt;/h2&gt;
&lt;p&gt;Running a function in JS by using parentheses &lt;code class=&quot;language-text&quot;&gt;()&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;variable-environment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#variable-environment&quot; aria-label=&quot;variable environment permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Variable Environment&lt;/h2&gt;
&lt;p&gt;Where the variable lives and how variables relate to each other in &lt;code class=&quot;language-text&quot;&gt;memory -&gt; scope&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;execution-stack-and-event-queue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#execution-stack-and-event-queue&quot; aria-label=&quot;execution stack and event queue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Execution Stack and Event Queue&lt;/h2&gt;
&lt;h2 id=&quot;dynamic-typing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dynamic-typing&quot; aria-label=&quot;dynamic typing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dynamic Typing&lt;/h2&gt;
&lt;p&gt;You do not tell the engine what type of data a variable holds. It figures it out while your code is running.&lt;/p&gt;
&lt;h2 id=&quot;primitive-type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#primitive-type&quot; aria-label=&quot;primitive type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Primitive type&lt;/h2&gt;
&lt;p&gt;A type of data that represents a single value, not an object. They are case-sensitive.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Undefined: represents a lack of existence&lt;/li&gt;
&lt;li&gt;Null&lt;/li&gt;
&lt;li&gt;Boolean: true or false&lt;/li&gt;
&lt;li&gt;Number: floating-point number (there are always some decimals)&lt;/li&gt;
&lt;li&gt;String: a sequence of characters (both ” and &quot;&quot; can use)&lt;/li&gt;
&lt;li&gt;Symbol&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;operator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#operator&quot; aria-label=&quot;operator permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Operator&lt;/h2&gt;
&lt;p&gt;A special function that is written differently. Generally, it takes two parameters and returns one result. For example, the &lt;code class=&quot;language-text&quot;&gt;+&lt;/code&gt; operator: &lt;code class=&quot;language-text&quot;&gt;+(3, 4) = 3 + 4&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Operator Precedence: which operator function gets called first. If there is more than one operator, the one with the highest precedence runs first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Associativity: the order operator functions get called in (left-to-right or right-to-left) when they have the same precedence.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;

a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//1. b = c &amp;lt;=&gt; b = 4&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//2. a = 4&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//3. return 4&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Surprising associativity example&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false, not true&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Why: (5 &gt; 4) &gt; 3 = true &gt; 3 = 1 &gt; 3 = false&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Math brain expects: &quot;5 &gt; 4 AND 4 &gt; 3&quot; = true&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// JS does: left-to-right evaluation&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;type-of-operator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#type-of-operator&quot; aria-label=&quot;type of operator permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Type of Operator&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Arithmetic Operator: &lt;code class=&quot;language-text&quot;&gt;+&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;-&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;*&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;++&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;--&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Comparison Operator: Not equal to (!=)&lt;/li&gt;
&lt;li&gt;Logical Operator: &amp;#x26;&amp;#x26;, ||, !&lt;/li&gt;
&lt;li&gt;String Operator: takes two strings as operands and creates a new string&lt;/li&gt;
&lt;li&gt;Ternary Operator: &lt;code class=&quot;language-text&quot;&gt;let status = (age &gt;= 18) ? &apos;adult&apos; : &apos;minor&apos;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;coercion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#coercion&quot; aria-label=&quot;coercion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Coercion&lt;/h2&gt;
&lt;p&gt;Converting a value from one type to another.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//&apos;12&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;hello&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//&apos;hello&apos; =&gt; return the first one that can be coerced to true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;statement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#statement&quot; aria-label=&quot;statement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Statement&lt;/h2&gt;
&lt;p&gt;Performs an action. Loops and &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; statements are examples of statements.&lt;/p&gt;
&lt;h2 id=&quot;expression&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#expression&quot; aria-label=&quot;expression permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Expression&lt;/h2&gt;
&lt;p&gt;A unit of code that results in a value. It does not have to be saved to a variable.&lt;/p&gt;
&lt;h2 id=&quot;this&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#this&quot; aria-label=&quot;this permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;this&lt;/h2&gt;
&lt;h3 id=&quot;invoking-the-function&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#invoking-the-function&quot; aria-label=&quot;invoking the function permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Invoking the function&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; is assigned to &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;, even when sitting inside a function. The short version: a function invocation like &lt;code class=&quot;language-text&quot;&gt;fn(...args)&lt;/code&gt; is the same as &lt;code class=&quot;language-text&quot;&gt;fn.call(window [ES5-strict: undefined], ...args)&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;invoking-the-method-inside-the-object&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#invoking-the-method-inside-the-object&quot; aria-label=&quot;invoking the method inside the object permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Invoking the method inside the object&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; will be the object.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// But caution if you reference the method of obj without calling it&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;fn1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//inner function&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;//closure&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; x
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;fn2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; f1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fn1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;f1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//this === obj&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; f2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fn2
&lt;span class=&quot;token function&quot;&gt;f2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//this === window&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;arguments&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#arguments&quot; aria-label=&quot;arguments permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Arguments&lt;/h2&gt;
&lt;p&gt;The parameters you pass to a function&lt;/p&gt;
&lt;h2 id=&quot;immediately-invoked-function-expressions-iife&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#immediately-invoked-function-expressions-iife&quot; aria-label=&quot;immediately invoked function expressions iife permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Immediately Invoked Function Expressions (IIFE)&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//function declaration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hello &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//function expression&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;greetFunc&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hello &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//immediately invoked function expression&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; greetFunc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hello &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;John&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//create function and run it&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;World&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;closure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#closure&quot; aria-label=&quot;closure permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Closure&lt;/h2&gt;
&lt;p&gt;A function retains access to variables in its outer scope, regardless of whether the outer function has finished running.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hello &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sayHi &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Hi&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Trung&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;bind-apply-call&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bind-apply-call&quot; aria-label=&quot;bind apply call permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bind, apply, call&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bind&lt;/th&gt;
&lt;th&gt;Create a copy of function with different this variable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apply, call&lt;/td&gt;
&lt;td&gt;Call attaches this into function and executes the function immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;John&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Doe&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;getFullName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastName
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;logName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Logged &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFullName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; logPersonName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;logPersonName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//bind&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;logName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;en&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//call&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;logName&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;en&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//apply&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Logged &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFullName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Trung&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Tuan&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;reflection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reflection&quot; aria-label=&quot;reflection permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reflection&lt;/h2&gt;
&lt;p&gt;An object can look at itself, listing and changing its properties and methods.&lt;/p&gt;
&lt;h2 id=&quot;function-constructor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#function-constructor&quot; aria-label=&quot;function constructor permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Function Constructor&lt;/h2&gt;
&lt;p&gt;A normal function that is used to construct objects. The &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; variable points to a new empty object, and that object is returned from the function automatically.&lt;/p&gt;
&lt;p&gt;For methods, you only need one copy, so add them to the prototype.&lt;/p&gt;</content:encoded></item></channel></rss>