{"componentChunkName":"component---src-templates-post-jsx","path":"/blog/improving-your-cls-and-lcp-core-web-vitals/","result":{"data":{"mdx":{"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nconst _frontmatter = {\n  \"title\": \"Improving your CLS and LCP Core Web Vitals\",\n  \"cover\": \"/blogs/webvitals/cover.svg\",\n  \"date\": \"2021-11-24\",\n  \"type\": [\"tutorial\"]\n};\nconst layoutProps = {\n  _frontmatter\n};\nconst MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  let {\n    components\n  } = _ref,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, `Web Vitals are a set of measurements that gauge the user experience of your website. If you score poorly\non them, your site is not as nice to use as a site with good Core Web Vitals. Google\nalso uses them to determine the SEO score of a page. So improving them is important for any website.`), mdx(\"h2\", {\n    \"id\": \"what-are-the-core-web-vitals\"\n  }, `What are the `, mdx(\"em\", {\n    parentName: \"h2\"\n  }, `Core`), ` Web Vitals?`), mdx(\"p\", null, `While there are seven different Web Vitals, the Core Web Vitals are the three that have the largest impact:`), mdx(\"h3\", {\n    \"id\": \"cls-cumulative-layout-shift\"\n  }, `CLS: Cumulative Layout Shift`), mdx(\"p\", null, `This measures how much your layout changes as the page loads, or as a user interacts with your page. Things like\nimages loading and pushing content down, or web fonts loading and causing your type to change will change the layout of\nyour page which can be frustrating to your visitors.`), mdx(\"p\", null, `You want your CLS to be below 0.1 to get a \"Good\" score.`), mdx(\"h3\", {\n    \"id\": \"fid-first-input-delay\"\n  }, `FID: First Input Delay`), mdx(\"p\", null, `This measures how soon your page is interactive by checking how fast your site responds to the first interaction a user\nhas with your page. While your page is loading it's too busy to process the event and only processes that after the\nloading is done.`), mdx(\"p\", null, `You want your FID to be below 100ms to get a \"Good\" score.`), mdx(\"h3\", {\n    \"id\": \"lcp-largest-contentful-paint\"\n  }, `LCP: Largest Contentful Paint`), mdx(\"p\", null, `This measures how long it takes your page to load visually. The Largest Contentful Paint is measured in the\ntime it takes for the last largest visual element to load. This doesn't have to be the visually largest element visible,\nor even the largest image. It can also be a block of text with a web font.`), mdx(\"p\", null, `You want your LCP to be below 2.5 seconds to get a \"Good\" score.`), mdx(\"p\", null, `Out of these three, CLS and LCP are influenced mostly by your page styling (and the way that is loaded) while FID essentially\nmeasures how much JS you execute on load.`), mdx(\"p\", null, `The other Web Vitals are FCP (First Contentful Paint), TTFB (Time To First Byte), TBT (Total Blocking Time) and TTI\n(Time To Interactive). This list will probably be expanded with Web Vitals that measure animation performance and overall responsiveness.`), mdx(\"h2\", {\n    \"id\": \"monitoring-your-core-web-vitals\"\n  }, `Monitoring your core web vitals`), mdx(\"p\", null, `To improve something you first need to measure it. Polypane continuously monitors the web vitals in each pane, but to not clutter the interface your score is hidden until you decide to show it.`), mdx(\"p\", null, `Click the settings icon in the top right of Polypane and then toggle \"Web Vitals status\" to turn web vitals on.`), mdx(\"img\", {\n    src: \"/blogs/webvitals/settings.png\",\n    className: \"imgshadow\",\n    alt: \"The settings panel with the `Web Vitals Status` option highlighted.\"\n  }), mdx(\"p\", null, \"With web vitals turned on each pane gets a new label that shows either a \", mdx(\"b\", {\n    style: {\n      color: 'green'\n    }\n  }, \"\\u25CF\"), \",\", ' ', mdx(\"b\", {\n    style: {\n      color: 'orange'\n    }\n  }, \"\\u25A0\"), \" or \", mdx(\"b\", {\n    style: {\n      color: 'red'\n    }\n  }, \"\\u25B2\"), \" for a \\\"good\\\", \\\"needs improvement\\\" or \\\"poor\\\" score respectively. If you score \\\"needs improvement\\\" or \\\"poor\\\" on any of the web vitals, the label also shows the Web Vitals that score \\\"poor\\\" or \\\"needs improvement\\\".\"), mdx(\"p\", null, `To get more detailed information you can hover over the label and a popup opens with a full overview of your Web Vitals. Along with the Core Web Vitals, Polypane also shows you the First Contentful Paint score and Time To First Byte.`), mdx(\"img\", {\n    src: \"/doc-img/webvitals.png\",\n    className: \"imgshadow\",\n    alt: \"The Web Vitals tooltip showing the different scores. First input delay is orange, because it scored 'needs improvement'.\"\n  }), mdx(\"p\", null, `This gives you a more detailed overview of your web vitals score. For any public page you'll notice a bunch of bars here\nalong with the score you get for each web vital.`), mdx(\"p\", null, `Those bars show you the average scoring for your page, from the CrUX Field Data that Google collects for all sites. Field Data tells you the average\nloading experience in the real world. The larger the green part of the bars for CLS and LCP are, the more people your page loads fast for.\nThe CrUX Field Data is measured separately for mobile and desktop, and Polypane will automatically switch between those depending\non the size of your pane.`), mdx(\"h3\", {\n    \"id\": \"why-crux-data-is-useful\"\n  }, `Why CrUX data is useful`), mdx(\"p\", null, `Web Vitals are non-deterministic so it's important to look at the averages. Your Web Vitals score will be different on each device, but also\ndifferent each time your reload your page. Sometimes the network might be a little bit slower, or your CPU might be doing\nsomething difficult right as your page is also being parsed, resulting in a worse Web Vitals score.`), mdx(\"p\", null, `By showing the averages you can quickly check if your experience compares to the majority of people loading your page, and if the majority of\nyour visitors have a good experience.`), mdx(\"h2\", {\n    \"id\": \"improving-cls-and-lcp\"\n  }, `Improving CLS and LCP`), mdx(\"p\", null, `Polypane offers visualizations for both the CLS and LCP so you can figure out exactly which elements contribute to a score\nand improve them one by one. For example on this page the LCP is the image at the top of the page and it has a score of 1.9s, which is \"good\".`), mdx(\"p\", null, `When a visualization is available there will be an `, mdx(Eye, {\n    width: 16,\n    height: 16,\n    mdxType: \"Eye\"\n  }), ` Eye icon visible in the popup next to the Web Vital. Click it to\ntoggle the visualization.`), mdx(\"h3\", {\n    \"id\": \"cumulative-layout-shift\"\n  }, `Cumulative Layout Shift`), mdx(\"p\", null, `The Cumulative Layout Shift visualization will show you how all the elements that contribute to the layout shift behaved\nfrom their initial point of rendering (semitransparent red rectangle with a dashed red border) to their ending position\n(transparent rectangle with a solid red border).`), mdx(\"video\", {\n    src: \"/blogs/polypane-7/cls-demo.mp4\",\n    controls: true,\n    className: \"imgshadow\",\n    style: {\n      margin: '2rem auto',\n      maxWidth: '100%'\n    }\n  }, \"Visualization of the Cumulative Layout Shift on the Polypane getting started page.\"), mdx(\"p\", null, `The bigger the change between the first and second rectangle, the higher your\nCLS score. For each of these types of elements you can improve your score by doing the following:`), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `Images`), ` (and video) could be loaded in too late, in which case you want to add the `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `loading=\"eager\"`), ` and `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `importance=\"high\"`), ` attributes to tell the browser to start loading these early.`, mdx(\"br\", null), mdx(\"br\", null), `Images can also be missing an aspect ratio or width and height attributes. When you add these the browser can already calculate the space an image will take before the image has loaded, so that wont cause a layout shift.`, mdx(\"br\", null), mdx(\"br\", null), `Also make sure that any hero images you have are preloaded at the top of the page using `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `<link rel=\"preload\" href=\"your-file.jpg\" />`), `.`, mdx(\"br\", null), mdx(\"br\", null))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `Text elements`), ` that result in a layout shift are usually caused by your webfont taking too long to load and the elements to first be rendered in the fallback font, then rendered again in the webfont. If your webfont is not important, switch your @font-face declaration from `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `display: swap;`), ` to `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `display: optional;`), `. This will give your web font a very small window to load (fast enough to not trigger a layout shift) or the browser will keep the fallback font and won't update to the new font. `, mdx(\"br\", null), mdx(\"br\", null), `You should also make sure to preload your font-files by adding them as `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `preload`), ` links in the header. This will tell your browsers that you're going to use this font, and the browser will download it in advance.`, mdx(\"br\", null), mdx(\"br\", null), ` There are also techniques to configure your fallback font using CSS Font Descriptors to make sure that there is no change in the layout when swapping the fonts. Making sure that works well with fallback fonts on all operating systems is impossible though, so we wont recommend it.`, mdx(\"br\", null), mdx(\"br\", null))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `Elements loaded in through JS`), ` are more difficult. When you can, make sure to reserve the space your content will take up (even if it's an estimate) to minimize the layout changing.`))), mdx(\"p\", null, `Keep in mind that multiple reloads of your page might show you different cumulative layout shift scores, and that different screen sizes will\nget different elements contributing to the CLS. So make sure to check multiple reloads and multiple screen sizes. In Polypane, you can do that both\nat the same time by using multiple panes.`), mdx(\"p\", null, `Even if you have a very low CLS the visualization can still show elements causing a CLS. You dont have to improve these,\nbut because the shifts might be small they could be easy to fix.`), mdx(\"h3\", {\n    \"id\": \"largest-contentful-paint\"\n  }, `Largest Contentful Paint`), mdx(\"p\", null, `The LCP visualization will highlight the element that causes the LCP. If your LCP is low or in the green then this is just for your\ninformation, there is nothing to do or to improve. But if your LCP is too high, in the orange or in the red, there's things you can\ndo to improve.`), mdx(\"img\", {\n    src: \"/blogs/webvitals/largest-contentful-paint.png\",\n    className: \"imgshadow\",\n    alt: \"The Web Vitals tooltip with the visualization for largest contentful paint turned on, showing a border around the image at the top of this page.\"\n  }), mdx(\"p\", null, `In general, make sure that your site loads as fast as possible. For this you can also use the Time To First Byte measurement in\nthe Web Vitals tooltip, which tells you how long it takes for the first bytes to load after you requested the page. In essence, how long\nit take your server to respond. If this takes very long it means your server is taking a long time to generate the page.`), mdx(\"p\", null, `There's many ways to make your server faster but that's a little out-of-scope for this article. Still, that\nwill have the biggest effect on your LCP.`), mdx(\"p\", null, `It's not just how fast your server responds with the HTML, but also all the other resources that need to get downloaded. For a good LCP you need to make sure that the CSS, webfonts and images that are required for your page load are downloaded as fast as possible.`), mdx(\"p\", null, `To do this you can make the same improvements as for CLS: Add a `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `loading=\"eager\"`), ` and `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `importance=\"high\"`), ` to your LCP image and make sure to preload that image in your `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `<head>`), ` using `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `<link rel=\"preload\" href=\"your-file.jpg\" />`), `. You should also do this when your LCP element has a large background image.`), mdx(\"p\", null, `If your LCP is a text block with a web font, make sure to preload that webfont as well: `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `<link rel=\"preload\" href=\"your-font.woff2\" />`), `.`), mdx(\"p\", null, `And if the load of your page depends on third party code, like Google Analytics, you can `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `preconnect`), ` and `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `dns-prefetch`), ` the domains they're on to get a faster response:`), mdx(\"div\", {\n    \"className\": \"gatsby-highlight\",\n    \"data-language\": \"html\"\n  }, mdx(\"pre\", _extends({\n    parentName: \"div\"\n  }, {\n    \"className\": \"language-html\"\n  }), mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-html\"\n  }), mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `<`), `link`), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-name\"\n  }), `rel`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-value\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation attr-equals\"\n  }), `=`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`), `preconnect`, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`)), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-name\"\n  }), `href`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-value\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation attr-equals\"\n  }), `=`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`), `https://googleanalytics.com/`, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`)), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `/>`)), `\n`, mdx(\"span\", _extends({\n    parentName: \"code\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token tag\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `<`), `link`), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-name\"\n  }), `rel`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-value\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation attr-equals\"\n  }), `=`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`), `dns-prefetch`, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`)), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-name\"\n  }), `href`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token attr-value\"\n  }), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation attr-equals\"\n  }), `=`), mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`), `https://googleanalytics.com/`, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `\"`)), ` `, mdx(\"span\", _extends({\n    parentName: \"span\"\n  }, {\n    \"className\": \"token punctuation\"\n  }), `/>`))))), mdx(\"p\", null, `This way your browser already knows you're going to connect to these domains and it can do the DNS lookup and connection before the request.`), mdx(\"p\", null, `Just knowing your Core Web Vitals score doesn't help you figure out what parts of your page to improve, but with the visualizations in Polypane the elements you should focus on become instantly clear. Check out your Core Web Vitals by `, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://dashboard.polypane/app/register/\"\n  }), `starting a free Polypane trial`), `.`));\n}\n;\nMDXContent.isMDXComponent = true;","timeToRead":6,"tableOfContents":{"items":[{"url":"#what-are-the-core-web-vitals","title":"What are the Core Web Vitals?","items":[{"url":"#cls-cumulative-layout-shift","title":"CLS: Cumulative Layout Shift"},{"url":"#fid-first-input-delay","title":"FID: First Input Delay"},{"url":"#lcp-largest-contentful-paint","title":"LCP: Largest Contentful Paint"}]},{"url":"#monitoring-your-core-web-vitals","title":"Monitoring your core web vitals","items":[{"url":"#why-crux-data-is-useful","title":"Why CrUX data is useful"}]},{"url":"#improving-cls-and-lcp","title":"Improving CLS and LCP","items":[{"url":"#cumulative-layout-shift","title":"Cumulative Layout Shift"},{"url":"#largest-contentful-paint","title":"Largest Contentful Paint"}]}]},"excerpt":"Web Vitals are a set of measurements that gauge the user experience of your website. If you score poorly\non them, your site is not as nice to use as a site with…","frontmatter":{"title":"Improving your CLS and LCP Core Web Vitals","cover":"/blogs/webvitals/cover.svg","date":"2021-11-24","updated":null},"fields":{"slug":"/blog/improving-your-cls-and-lcp-core-web-vitals/","date":"2021-11-23T23:00:00.000Z","ogFileName":"improving-your-cls-and-lcp-core-web-vitals"}}},"pageContext":{"slug":"/blog/improving-your-cls-and-lcp-core-web-vitals/"}},"staticQueryHashes":["4164364741","425175329"]}