{"componentChunkName":"component---src-templates-post-js","path":"/blog/2020/04/13/lighthouse-ci/","result":{"data":{"post":{"html":"<p>You’ve spent many hours trying to improve your web performance and have got it to a good speed. What happens next? How do you ensure it remains in good shape especially if you’re working in a team with diverse backgrounds and level of coding.</p>\n<!--more-->\n<h2 id=\"web-performance-must-be-monitored\" style=\"position:relative;\"><a href=\"#web-performance-must-be-monitored\" aria-label=\"web performance must be monitored permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Web performance must be monitored</h2>\n<p>I truly believe web performance shouldn’t be an item in your backlog, rather part of acceptance criteria on each story. That said, having discipline is hard when it comes to web performance. Each member of the team should be aware of dos and don’ts so that they can right code which doesn’t add a few seconds to current page load time, or user’s interactivity with the page.</p>\n<p>In order to do so, you need to constantly monitor the performance of your application and when is a better time to do it than your CI/CD pipeline or when creating a pull request to get the code merged in (it’s worth mentioning having a CI/CD pipeline is not necessary to have a high quality, well written and performing site).</p>\n<p>In addition to all of that, your site performs differently from your localhost, so you need to ensure you’re testing close to your actual environment.</p>\n<h2 id=\"lighthouse-ci\" style=\"position:relative;\"><a href=\"#lighthouse-ci\" aria-label=\"lighthouse ci permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Lighthouse CI</h2>\n<p><a href=\"https://github.com/GoogleChrome/lighthouse-ci\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Lighthouse CI</a> is a set of commands that make continuously running, asserting, saving, and retrieving Lighthouse results as easy as possible.</p>\n<p>This is a great tool which allows you to run audits on your code base using command line, and raise an alert when someone unintentionally downgrades the performance.</p>\n<h2 id=\"what-to-measure\" style=\"position:relative;\"><a href=\"#what-to-measure\" aria-label=\"what to measure permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>What to measure?</h2>\n<p>There are countless metrics on which we could focus on. Some are user centric like time to interact, some are resource centric such as the size of your <em>JavaScript</em> or <em>CSS</em> files. Lighthouse CI allows us to easily focus on the size of the files. You can create two types of budgets:</p>\n<p>1- <strong>Number of given resource</strong>: This will help you to for example enforce having only <code class=\"language-text\">3</code> <em>JavaScript</em> and <code class=\"language-text\">1</code> <em>CSS</em> file.\r\n2- <strong>Size of a given resource</strong>: This budget will enforce that a given resource is of certain size, e.g. only <code class=\"language-text\">500kb</code>.</p>\n<h2 id=\"how-does-it-know\" style=\"position:relative;\"><a href=\"#how-does-it-know\" aria-label=\"how does it know permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>How does it know?</h2>\n<p>You’re probably wondering how does <strong>Lighthouse CI</strong> know what to measure and when to raise an alert. This **||is done via a <code class=\"language-text\">JSON</code> file which is usually named <code class=\"language-text\">budget.json</code>, but you can choose any name you prefer.</p>\n<h2 id=\"performance-budget\" style=\"position:relative;\"><a href=\"#performance-budget\" aria-label=\"performance budget permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Performance budget</h2>\n<p>The content of this file is an array of objects representing the budgets for a path. The content will look like below if you only have one path to assert:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"25649810326442734000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`[\r\n  {\r\n    &quot;resourceSizes&quot;: [\r\n      ...\r\n    ],\r\n    &quot;resourceCounts&quot;: [\r\n      ...\r\n    ]\r\n  }\r\n]`, `25649810326442734000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">[</span>\r\n  <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"resourceSizes\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceCounts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">]</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>Within the <code class=\"language-text\">resourceSizes</code> and <code class=\"language-text\">resourceCounts</code> arrays, you can add individual objects that specify the <code class=\"language-text\">resourceType</code> and its budget. The budget can be any positive integer and the <code class=\"language-text\">resourceType</code> can be any of the following:</p>\n<table>\n<thead>\n<tr>\n<th><strong>Resource type</strong></th>\n<th><strong>Description</strong></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>script</strong></td>\n<td>JavaScript files</td>\n</tr>\n<tr>\n<td><strong>document</strong></td>\n<td>HTML documents</td>\n</tr>\n<tr>\n<td><strong>image</strong></td>\n<td>Images</td>\n</tr>\n<tr>\n<td><strong>stylesheet</strong></td>\n<td>CSS files</td>\n</tr>\n<tr>\n<td><strong>font</strong></td>\n<td>Webfonts</td>\n</tr>\n<tr>\n<td><strong>media</strong></td>\n<td>Other media</td>\n</tr>\n<tr>\n<td><strong>other</strong></td>\n<td>Any other resource which doesn’t match the above, e.g. data transfers over Websocket connections</td>\n</tr>\n<tr>\n<td><strong>third-party</strong></td>\n<td>All resources from a third-party domain</td>\n</tr>\n<tr>\n<td><strong>total</strong></td>\n<td>All resources combined</td>\n</tr>\n</tbody>\n</table>\n<p>For example, the following file will set a 200Kb budget for all scripts, a 500Kb for all images, and a 500Kb for all other resources. Then it sets the total network requests to be 25.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"11276160670835433000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`[\r\n  {\r\n    &quot;resourceSizes&quot;: [\r\n      {\r\n        &quot;resourceType&quot;: &quot;script&quot;,\r\n        &quot;budget&quot;: 200\r\n      },\r\n      {\r\n        &quot;resourceType&quot;: &quot;image&quot;,\r\n        &quot;budget&quot;: 500\r\n      },\r\n      {\r\n        &quot;resourceType&quot;: &quot;total&quot;,\r\n        &quot;budget&quot;: 500\r\n      }\r\n    ],\r\n    &quot;resourceCounts&quot;: [\r\n      {\r\n        &quot;resourceType&quot;: &quot;total&quot;,\r\n        &quot;budget&quot;: 25\r\n      }\r\n    ]\r\n  }\r\n]`, `11276160670835433000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">[</span>\r\n  <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"resourceSizes\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      <span class=\"token punctuation\">{</span>\r\n        <span class=\"token property\">\"resourceType\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"script\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token property\">\"budget\"</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span>\r\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token punctuation\">{</span>\r\n        <span class=\"token property\">\"resourceType\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"image\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token property\">\"budget\"</span><span class=\"token operator\">:</span> <span class=\"token number\">500</span>\r\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token punctuation\">{</span>\r\n        <span class=\"token property\">\"resourceType\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"total\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token property\">\"budget\"</span><span class=\"token operator\">:</span> <span class=\"token number\">500</span>\r\n      <span class=\"token punctuation\">}</span>\r\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceCounts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      <span class=\"token punctuation\">{</span>\r\n        <span class=\"token property\">\"resourceType\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"total\"</span><span class=\"token punctuation\">,</span>\r\n        <span class=\"token property\">\"budget\"</span><span class=\"token operator\">:</span> <span class=\"token number\">25</span>\r\n      <span class=\"token punctuation\">}</span>\r\n    <span class=\"token punctuation\">]</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">]</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>As of <strong>Lighthouse</strong> 5.3 you can add a <code class=\"language-text\">path</code> property to select which pages the budget should apply to. This is very handy because it allows you to have a centralised budget file for all your pages.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"37148249262368060000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`[\r\n  {\r\n    &quot;path&quot;:&quot;/*&quot;,\r\n    &quot;resourceSizes&quot;: [\r\n      ...\r\n    ],\r\n    &quot;resourceCounts&quot;: [\r\n      ...\r\n    ]\r\n  },\r\n  {\r\n    &quot;path&quot;:&quot;/blog&quot;,\r\n    &quot;resourceSizes&quot;: [\r\n      ...\r\n    ],\r\n    &quot;resourceCounts&quot;: [\r\n      ...\r\n    ]\r\n  },\r\n  {\r\n    &quot;path&quot;:&quot;/contact&quot;,\r\n    &quot;resourceSizes&quot;: [\r\n      ...\r\n    ],\r\n    &quot;resourceCounts&quot;: [\r\n      ...\r\n    ]\r\n  }\r\n]`, `37148249262368060000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">[</span>\r\n  <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"path\"</span><span class=\"token operator\">:</span><span class=\"token string\">\"/*\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceSizes\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceCounts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span>\r\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"path\"</span><span class=\"token operator\">:</span><span class=\"token string\">\"/blog\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceSizes\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceCounts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span>\r\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n  <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"path\"</span><span class=\"token operator\">:</span><span class=\"token string\">\"/contact\"</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceSizes\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"resourceCounts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\r\n      ...\r\n    <span class=\"token punctuation\">]</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">]</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<h2 id=\"how-should-you-choose-your-numbers\" style=\"position:relative;\"><a href=\"#how-should-you-choose-your-numbers\" aria-label=\"how should you choose your numbers permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>How should you choose your numbers?</h2>\n<p>Finding the performance budget is not an easy task since it will depend on your application’s structure and bundling style. However, you can use this useful tool called <a href=\"https://perf-budget-calculator.firebaseapp.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><strong>Performance Budget Calculator</strong></a> to have a starting point. You might have to run the tool multiple time to each to a point where you’d be happy with the result. You don’t want to be too aggressive and also too complacent either.</p>\n<h2 id=\"using-the-ci-locally\" style=\"position:relative;\"><a href=\"#using-the-ci-locally\" aria-label=\"using the ci locally permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Using the CI locally</h2>\n<p>You can install the CI locally with:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"28686428566898446000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`npm install -g @lhci/cli`, `28686428566898446000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> <span class=\"token parameter variable\">-g</span> @lhci/cli</code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>It also has a dashboard which shows you the historical runs, compares reports and store their results.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"24203219055803093000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`npm install @lhci/server`, `24203219055803093000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> @lhci/server</code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>Before running the CI, you need to have a configuration file to tell the tool what to test and where to find the budget file (you could also pass those individually without a config file, but it’d be a pain):</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"97885388790054930000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`// lighthouserc.json\r\n{\r\n  &quot;ci&quot;: {\r\n    &quot;collect&quot;: {\r\n      &quot;numberOfRuns&quot;: 2,\r\n      &quot;startServerCommand&quot;: &quot;yarn serve&quot;,\r\n      &quot;url&quot;: &quot;http://localhost:8080&quot;\r\n    },\r\n    &quot;preset&quot;: &quot;lighthouse:recommended&quot;\r\n  }\r\n}`, `97885388790054930000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token comment\">// lighthouserc.json</span>\r\n<span class=\"token punctuation\">{</span>\r\n  <span class=\"token property\">\"ci\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n    <span class=\"token property\">\"collect\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\r\n      <span class=\"token property\">\"numberOfRuns\"</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"startServerCommand\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"yarn serve\"</span><span class=\"token punctuation\">,</span>\r\n      <span class=\"token property\">\"url\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"http://localhost:8080\"</span>\r\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\r\n    <span class=\"token property\">\"preset\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"lighthouse:recommended\"</span>\r\n  <span class=\"token punctuation\">}</span>\r\n<span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>Then you can run the tool on your local web server by first running:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"63879864146989030000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`lhci autorun --config=./lighthouserc.json`, `63879864146989030000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\">lhci autorun <span class=\"token parameter variable\">--config</span><span class=\"token operator\">=</span>./lighthouserc.json</code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>and then:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"77111024990371600000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`lhci assert --no-lighthouserc --budgetsFile=budget.json`, `77111024990371600000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\">lhci assert --no-lighthouserc <span class=\"token parameter variable\">--budgetsFile</span><span class=\"token operator\">=</span>budget.json</code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>And you will get a result similar to below based on your app’s performance:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"32366766976500716000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`2 result(s) for http://localhost:8080/\r\n\r\n  ×  resource-summary.script.size failure for maxNumericValue assertion\r\n     Keep request counts low and transfer sizes small\r\n     Documentation: https://developers.google.com/web/tools/lighthouse/audits/budgets\r\n\r\n        expected: <=102400\r\n           found: 2416174\r\n      all values: 2416174, 2416174\r\n\r\n\r\n  ×  resource-summary.total.size failure for maxNumericValue assertion\r\n     Keep request counts low and transfer sizes small\r\n     Documentation: https://developers.google.com/web/tools/lighthouse/audits/budgets\r\n\r\n        expected: <=512000\r\n           found: 2437694\r\n      all values: 2437694, 2437694\r\n\r\nAssertion failed. Exiting with status code 1.`, `32366766976500716000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\"><span class=\"token number\">2</span> result<span class=\"token punctuation\">(</span>s<span class=\"token punctuation\">)</span> <span class=\"token keyword\">for</span> http://localhost:8080/\r\n\r\n  ×  resource-summary.script.size failure <span class=\"token keyword\">for</span> maxNumericValue assertion\r\n     Keep request counts low and transfer sizes small\r\n     Documentation: https://developers.google.com/web/tools/lighthouse/audits/budgets\r\n\r\n        expected: <span class=\"token operator\">&lt;=</span><span class=\"token number\">102400</span>\r\n           found: <span class=\"token number\">2416174</span>\r\n      all values: <span class=\"token number\">2416174</span>, <span class=\"token number\">2416174</span>\r\n\r\n\r\n  ×  resource-summary.total.size failure <span class=\"token keyword\">for</span> maxNumericValue assertion\r\n     Keep request counts low and transfer sizes small\r\n     Documentation: https://developers.google.com/web/tools/lighthouse/audits/budgets\r\n\r\n        expected: <span class=\"token operator\">&lt;=</span><span class=\"token number\">512000</span>\r\n           found: <span class=\"token number\">2437694</span>\r\n      all values: <span class=\"token number\">2437694</span>, <span class=\"token number\">2437694</span>\r\n\r\nAssertion failed. Exiting with status code <span class=\"token number\">1</span>.</code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<h2 id=\"using-the-tool-on-your-prs\" style=\"position:relative;\"><a href=\"#using-the-tool-on-your-prs\" aria-label=\"using the tool on your prs permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Using the tool on your PRs</h2>\n<p>I am focusing on <a href=\"https://github.com/features/actions\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">GitHub</a> using actions here. But you can apply this technique to <a href=\"https://azure.microsoft.com/en-au/services/devops/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Azure DevOps</a>, <a href=\"https://circleci.com/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CircleCI</a>, <a href=\"https://www.jetbrains.com/teamcity/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">TeamCity</a>, or any other tool you’re using. For this to work you will need to have a <a href=\"https://guides.github.com/introduction/flow/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">GitHub flow</a> in your <code class=\"language-text\">git</code> repository defined.</p>\n<p>This article’s focus is not to teach you how to create a workflow, but here is the bare minimum you will need to have. Create a <code class=\"language-text\">.yaml</code>/<code class=\"language-text\">.yml</code> file in the <code class=\"language-text\">.github/workflows</code> folder, you can name it whatever you want. I usually name them after what’s their purpose, e.g. <code class=\"language-text\">lighthouse.yml</code> or <code class=\"language-text\">lhci-performance-bot.yml</code>.</p>\n<p>But before we get to the content of our <code class=\"language-text\">yaml</code> file, let’s see what we need to do on each PR:</p>\n<ol>\n<li>Pull down the code</li>\n<li>Build the app</li>\n<li>Deploy to a temporary site (I am using Netlify Preview here, but you can use others like staging slots on Azure App Service, etc)</li>\n<li>Run Lighthouse CI on the site</li>\n<li>Fail if the conditions are not met</li>\n</ol>\n<p>For this to work, we need an application. So I just spun up a new <a href=\"https://vuejs.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Vue</a> application using <a href=\"https://cli.vuejs.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Vue CLI</a>, then added my workflow to it and pushed it to <a href=\"https://github.com/yashints/lighthouse-ci-pr-action\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">a new GitHub repo</a>.</p>\n<p>Here is how my workflow looks like:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"99288361220041770000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`name: Lighthouse CI\r\n\r\non:\r\n  pull_request:\r\n    branches: [master]\r\n\r\njobs:\r\n  lighthouse:\r\n    runs-on: ubuntu-latest\r\n\r\n    steps:\r\n      - uses: actions/checkout@v2\r\n      - name: Setup Node 12\r\n        uses: actions/setup-node@v1\r\n        with:\r\n          node-version: 12.x\r\n      - name: Yarn Install and Build\r\n        run: |\r\n          yarn\r\n          yarn build\r\n      \r\n      - name: Waiting for 200 from the Netlify Preview\r\n        uses: jakepartusch/wait-for-netlify-action@v1\r\n        id: waitFor200\r\n        with:\r\n          site_name: &quot;lighthouse-ci-pr&quot;\r\n\r\n      - name: Audit URLs using Lighthouse\r\n        uses: treosh/lighthouse-ci-action@v2\r\n        with:\r\n          urls: |\r\n            \\$DEPLOY_URL/\r\n            \\$DEPLOY_URL/about\r\n          runs: 3\r\n          budgetPath: ./budget.json\r\n        env:\r\n          DEPLOY_URL: \\${{ steps.waitFor200.outputs.url }}`, `99288361220041770000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yml\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-yml line-numbers\"><code class=\"language-yml\"><span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Lighthouse CI\r\n\r\n<span class=\"token key atrule\">on</span><span class=\"token punctuation\">:</span>\r\n  <span class=\"token key atrule\">pull_request</span><span class=\"token punctuation\">:</span>\r\n    <span class=\"token key atrule\">branches</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>master<span class=\"token punctuation\">]</span>\r\n\r\n<span class=\"token key atrule\">jobs</span><span class=\"token punctuation\">:</span>\r\n  <span class=\"token key atrule\">lighthouse</span><span class=\"token punctuation\">:</span>\r\n    <span class=\"token key atrule\">runs-on</span><span class=\"token punctuation\">:</span> ubuntu<span class=\"token punctuation\">-</span>latest\r\n\r\n    <span class=\"token key atrule\">steps</span><span class=\"token punctuation\">:</span>\r\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">uses</span><span class=\"token punctuation\">:</span> actions/checkout@v2\r\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Setup Node 12\r\n        <span class=\"token key atrule\">uses</span><span class=\"token punctuation\">:</span> actions/setup<span class=\"token punctuation\">-</span>node@v1\r\n        <span class=\"token key atrule\">with</span><span class=\"token punctuation\">:</span>\r\n          <span class=\"token key atrule\">node-version</span><span class=\"token punctuation\">:</span> 12.x\r\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Yarn Install and Build\r\n        <span class=\"token key atrule\">run</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">|</span><span class=\"token scalar string\">\r\n          yarn\r\n          yarn build</span>\r\n      \r\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Waiting for 200 from the Netlify Preview\r\n        <span class=\"token key atrule\">uses</span><span class=\"token punctuation\">:</span> jakepartusch/wait<span class=\"token punctuation\">-</span>for<span class=\"token punctuation\">-</span>netlify<span class=\"token punctuation\">-</span>action@v1\r\n        <span class=\"token key atrule\">id</span><span class=\"token punctuation\">:</span> waitFor200\r\n        <span class=\"token key atrule\">with</span><span class=\"token punctuation\">:</span>\r\n          <span class=\"token key atrule\">site_name</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"lighthouse-ci-pr\"</span>\r\n\r\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Audit URLs using Lighthouse\r\n        <span class=\"token key atrule\">uses</span><span class=\"token punctuation\">:</span> treosh/lighthouse<span class=\"token punctuation\">-</span>ci<span class=\"token punctuation\">-</span>action@v2\r\n        <span class=\"token key atrule\">with</span><span class=\"token punctuation\">:</span>\r\n          <span class=\"token key atrule\">urls</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">|</span><span class=\"token scalar string\">\r\n            $DEPLOY_URL/\r\n            $DEPLOY_URL/about</span>\r\n          <span class=\"token key atrule\">runs</span><span class=\"token punctuation\">:</span> <span class=\"token number\">3</span>\r\n          <span class=\"token key atrule\">budgetPath</span><span class=\"token punctuation\">:</span> ./budget.json\r\n        <span class=\"token key atrule\">env</span><span class=\"token punctuation\">:</span>\r\n          <span class=\"token key atrule\">DEPLOY_URL</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> steps.waitFor200.outputs.url <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>What is happening is that I am just creating an action on each PR. In this action, we need to pull down the code, install node, run <code class=\"language-text\">yarn</code> and <code class=\"language-text\">yarn build</code> to build the application, wait for Netlify to deploy our PR into a preview site, then get the URL and inject it into the next step.</p>\n<p>I’m just using <a href=\"https://github.com/marketplace/actions/wait-for-netlify\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"language-text\">wait-for-netlify-action</code></a>, but it’s not that hard to implement this part. Then we will be using the <a href=\"https://github.com/treosh/lighthouse-ci-action/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"language-text\">lighthouse-ci-action</code></a> to run our audit on the paths we have specified.</p>\n<p>Notice I am not using a <strong>Lighthouse</strong> configuration file in this workflow because it’s not needed. You can pass the parameters directly to the action like I’ve done above. I am just auditing the home and about pages since I’ve only got these two pages.</p>\n<p>You can pass as many URL’s to this as you like, but if there are many of the, it makes more sense to add the config file back.</p>\n<blockquote>\n<p>💡 Remember to setup Netlify for your site and set it to create a deployment for every PR. Then get the site id and pass it to the <code class=\"language-text\">wait-for-netlify-action</code> action.</p>\n</blockquote>\n<p><strong>Pro tip</strong>: When you setup your Vue CLI generated application which is using Vue Router, the non default routes won’t work if the user types them in the browser URL bar and hits enter. That’s because that route is handled on the client side and server doesn’t know about it. To fix that you need to add a URL rewrite rule to return your <code class=\"language-text\">index.html</code> for all legitimate routes.</p>\n<p>In case of Netlify, just create a file named <code class=\"language-text\">_redirects</code> inside your <em>public</em> and add the following line to it.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"56249322138618200000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied to clipboard ✅\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`/*    /index.html   200`, `56249322138618200000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                Copy<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-bash line-numbers\"><code class=\"language-bash\">/*    /index.html   <span class=\"token number\">200</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<h2 id=\"result\" style=\"position:relative;\"><a href=\"#result\" aria-label=\"result permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Result</h2>\n<p>I created a branch and pushed it up. Then created a PR and voila, the action kicked in and failed, since I have a large image on the about page.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1080px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/6d8fde67ffb5d7e17f0cfe9e2c0d8100/4e663/ci-results.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 44.074074074074076%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFBP/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHTHrQkeID/xAAcEAAABgMAAAAAAAAAAAAAAAAAAQIDBBMFEDL/2gAIAQEAAQUCmmpBWui5wZDnX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAIDAQEAAAAAAAAAAAAAAAACATOREBL/2gAIAQEABj8CXy0wWNpY2id//8QAGBABAQADAAAAAAAAAAAAAAAAARAhMaH/2gAIAQEAAT8hymy6bJXQ3//aAAwDAQACAAMAAAAQbD//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAACAQQDAAAAAAAAAAAAAAABEQAQIVHRMZHw/9oACAEBAAE/EGaztl2GFCDZ3CTnsz1cCv8A/9k='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"PR validation failed on GitHub action using lighthouse CI&#39;s audit\"\n        title=\"\"\n        src=\"/static/6d8fde67ffb5d7e17f0cfe9e2c0d8100/47311/ci-results.jpg\"\n        srcset=\"/static/6d8fde67ffb5d7e17f0cfe9e2c0d8100/6f81f/ci-results.jpg 270w,\n/static/6d8fde67ffb5d7e17f0cfe9e2c0d8100/09d21/ci-results.jpg 540w,\n/static/6d8fde67ffb5d7e17f0cfe9e2c0d8100/47311/ci-results.jpg 1080w,\n/static/6d8fde67ffb5d7e17f0cfe9e2c0d8100/0047d/ci-results.jpg 1620w,\n/static/6d8fde67ffb5d7e17f0cfe9e2c0d8100/4e663/ci-results.jpg 1893w\"\n        sizes=\"(max-width: 1080px) 100vw, 1080px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>I then reduced the size of the image and pushed the changes up. This time it passed.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1080px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/63e3628409b060661f7e8007c50a4a98/98d66/auditsuccess.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 41.11111111111111%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAIABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAbMsQD//xAAZEAACAwEAAAAAAAAAAAAAAAAAAwECFAT/2gAIAQEAAQUC62XVGtxrcf/EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAICAwAAAAAAAAAAAAAAAAACMqESMWH/2gAIAQEABj8CXFt8JUSo/8QAGBAAAwEBAAAAAAAAAAAAAAAAAAFhkRH/2gAIAQEAAT8hbyF0xPgmwf/aAAwDAQACAAMAAAAQf/8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxAAAQQDAAAAAAAAAAAAAAAAAQARUfBBYcH/2gAIAQEAAT8QKagCWYAl1cuLV2hf/9k='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"PR validation passing on GitHub action using lighthouse CI\"\n        title=\"\"\n        src=\"/static/63e3628409b060661f7e8007c50a4a98/47311/auditsuccess.jpg\"\n        srcset=\"/static/63e3628409b060661f7e8007c50a4a98/6f81f/auditsuccess.jpg 270w,\n/static/63e3628409b060661f7e8007c50a4a98/09d21/auditsuccess.jpg 540w,\n/static/63e3628409b060661f7e8007c50a4a98/47311/auditsuccess.jpg 1080w,\n/static/63e3628409b060661f7e8007c50a4a98/0047d/auditsuccess.jpg 1620w,\n/static/63e3628409b060661f7e8007c50a4a98/98d66/auditsuccess.jpg 1905w\"\n        sizes=\"(max-width: 1080px) 100vw, 1080px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h2 id=\"summary\" style=\"position:relative;\"><a href=\"#summary\" aria-label=\"summary permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Summary</h2>\n<p>We learned together how to use Lighthouse CI, a tool which allows you to run audits on your sites from command line in our CI/CD pipeline to make sure no one is downgrading the performance when they work on the application.</p>\n<p>This way, we’re always on top of our game rather than creating backlog items to find the issues and fix them later.</p>\n<p>In addition to that, your users are going to be happy and remain happy with their experience which is worth all the effort.</p>\n<p>Hope you’ve enjoyed reading this and happy performance enforcement 👋🏽.</p>","timeToRead":6,"frontmatter":{"unformattedDate":"2020-04-13T00:00:00.000Z","date":"April 13, 2020","title":"Stay on top of performance with Lighthouse CI 🚦","subtitle":null,"id":null,"author":"Yaser Adel Mehraban","gravatar":null,"canonical_url":null,"tags":["webdev","showdev","performance","lighthouse"],"thumbnail":null,"cover":{"childImageSharp":{"gatsbyImageData":{"layout":"constrained","placeholder":{"fallback":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8klEQVR42iWO6W7aQBSF/c55iv6vFKmv0B9RVNEqRTRiSWzjBQMBbLzv68x4Bc9goo5b6dPR0dU9515m50It7c5ZR9UGFx9e/+OhqwMofVj2cdVHFY5KnNYkrLAP+whd7bRi5Lg5Jq3iwX1U74PynNRGUplprUXIyBo96+jEzlsjQk5WmzHUY2SkddKQAF0ZLm4WevxjJc8255moTvn9QtHmsjrldk8vi8mft4V84g7WlN+9ioefc/5V3M/lox6X9D7DBqUM8LYgakHscnCqwauHoL171bAxw60Tuc3dbz/99m7XN6+5R93oDTTQfxk2LKXkIjilYKGNVx/iTi+uNiJueUsun1F7t9DNQsSC2ITYgJiaM+hVQD5iGjZzyUKCDkWnlNxachvZrfd+o+e9WWATYL0gGiAaIiocGU2BNUj2Y1hNNzZSLCCY5fs5f9cyVssFpxHcRhxpRa8Vw07MegkQISErA8tJfwQ03DCcidZvrDJ9XmvpL06dsMcXyZDotteIfisG7ah+S7t28PJ9Mjw8DL+PvVLidVAzolNNZuzj10feyNcW4G3IO4iGJb+Vgn9QQ7vCyzbrnpf9l2+3pd0rBWYtxKw+gnctn58yzig4PWfNgnUgd045Nea0hDtRTVkLsDak8HYu+oBzIGvC5Sn5CzVfDZfKEd37AAAAAElFTkSuQmCC"},"backgroundColor":"transparent","images":{"fallback":{"src":"/static/99bb498c579158cd132524c66b273a1a/a2195/socialpreview.png","srcSet":"/static/99bb498c579158cd132524c66b273a1a/cadb4/socialpreview.png 750w,\n/static/99bb498c579158cd132524c66b273a1a/a2195/socialpreview.png 800w","sizes":"(min-width: 800px) 800px, 100vw"},"sources":[{"srcSet":"/static/99bb498c579158cd132524c66b273a1a/11ba1/socialpreview.webp 750w,\n/static/99bb498c579158cd132524c66b273a1a/5f042/socialpreview.webp 800w","type":"image/webp","sizes":"(min-width: 800px) 800px, 100vw"}]},"width":800,"height":400}}},"img":null,"socialPreview":{"publicURL":"/static/99bb498c579158cd132524c66b273a1a/socialpreview.png"}}}},"pageContext":{"title":"Stay on top of performance with Lighthouse CI 🚦","postPath":"/blog/2020/04/13/lighthouse-ci","nextPost":"/blog/2020/04/24/iterators","previousPost":"/blog/2020/04/05/git-logs"}},"staticQueryHashes":[],"slicesMap":{}}