CINXE.COM
Script evaluation and long tasks | Articles | web.dev
<!doctype html> <html lang="en" dir="ltr"> <head> <meta name="google-signin-client-id" content="157101835696-ooapojlodmuabs2do2vuhhnf90bccmoi.apps.googleusercontent.com"> <meta name="google-signin-scope" content="profile email https://www.googleapis.com/auth/developerprofiles https://www.googleapis.com/auth/developerprofiles.award"> <meta property="og:site_name" content="web.dev"> <meta property="og:type" content="website"><meta name="theme-color" content="#3740ff"><meta charset="utf-8"> <meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="manifest" href="/_pwa/web/manifest.json" crossorigin="use-credentials"> <link rel="preconnect" href="//www.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.googleapis.com" crossorigin> <link rel="preconnect" href="//apis.google.com" crossorigin> <link rel="preconnect" href="//www.google-analytics.com" crossorigin><link rel="stylesheet" href="//fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"> <link rel="stylesheet" href="//fonts.googleapis.com/css2?family=Material+Icons&family=Material+Symbols+Outlined&display=block"><link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/css/app.css"> <link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/css/dark-theme.css" disabled> <link rel="shortcut icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/favicon.png"> <link rel="apple-touch-icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/touchicon-180.png"><link rel="canonical" href="https://web.dev/articles/script-evaluation-and-long-tasks"><link rel="search" type="application/opensearchdescription+xml" title="web.dev" href="https://web.dev/s/opensearch.xml"> <link rel="alternate" hreflang="en" href="https://web.dev/articles/script-evaluation-and-long-tasks" /><link rel="alternate" hreflang="x-default" href="https://web.dev/articles/script-evaluation-and-long-tasks" /><link rel="alternate" hreflang="ar" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=ar" /><link rel="alternate" hreflang="bn" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=bn" /><link rel="alternate" hreflang="zh-Hans" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=zh-tw" /><link rel="alternate" hreflang="fa" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=fa" /><link rel="alternate" hreflang="fr" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=fr" /><link rel="alternate" hreflang="de" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=de" /><link rel="alternate" hreflang="he" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=he" /><link rel="alternate" hreflang="hi" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=hi" /><link rel="alternate" hreflang="id" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=id" /><link rel="alternate" hreflang="it" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=it" /><link rel="alternate" hreflang="ja" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=ja" /><link rel="alternate" hreflang="ko" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=ko" /><link rel="alternate" hreflang="pl" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=pl" /><link rel="alternate" hreflang="pt-BR" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=pt-br" /><link rel="alternate" hreflang="ru" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=ru" /><link rel="alternate" hreflang="es-419" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=es-419" /><link rel="alternate" hreflang="th" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=th" /><link rel="alternate" hreflang="tr" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=tr" /><link rel="alternate" hreflang="vi" href="https://web.dev/articles/script-evaluation-and-long-tasks?hl=vi" /><link rel="alternate" hreflang="en-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks" /><link rel="alternate" hreflang="x-default" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks" /><link rel="alternate" hreflang="ar-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=ar" /><link rel="alternate" hreflang="bn-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=bn" /><link rel="alternate" hreflang="zh-Hans-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=zh-tw" /><link rel="alternate" hreflang="fa-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=fa" /><link rel="alternate" hreflang="fr-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=fr" /><link rel="alternate" hreflang="de-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=de" /><link rel="alternate" hreflang="he-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=he" /><link rel="alternate" hreflang="hi-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=hi" /><link rel="alternate" hreflang="id-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=id" /><link rel="alternate" hreflang="it-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=it" /><link rel="alternate" hreflang="ja-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=ja" /><link rel="alternate" hreflang="ko-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=ko" /><link rel="alternate" hreflang="pl-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=pl" /><link rel="alternate" hreflang="pt-BR-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=pt-br" /><link rel="alternate" hreflang="ru-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=ru" /><link rel="alternate" hreflang="es-419-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=es-419" /><link rel="alternate" hreflang="th-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=th" /><link rel="alternate" hreflang="tr-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=tr" /><link rel="alternate" hreflang="vi-cn" href="https://web.developers.google.cn/articles/script-evaluation-and-long-tasks?hl=vi" /><title>Script evaluation and long tasks | Articles | web.dev</title> <meta property="og:title" content="Script evaluation and long tasks | Articles | web.dev"><meta name="description" content="When loading scripts, it takes time for the browser to evaluate them prior to execution, which can cause long tasks. Learn how script evaluation works, and what you can do to keep it from causing long tasks during page load."> <meta property="og:description" content="When loading scripts, it takes time for the browser to evaluate them prior to execution, which can cause long tasks. Learn how script evaluation works, and what you can do to keep it from causing long tasks during page load."><meta property="og:url" content="https://web.dev/articles/script-evaluation-and-long-tasks"><meta property="og:image" content="https://web.dev/static/articles/script-evaluation-and-long-tasks/image/thumb.png"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="675"><meta property="og:locale" content="en"><meta name="twitter:card" content="summary_large_image"><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "dateModified": "2023-05-09", "headline": "Script evaluation and long tasks" } </script><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{ "@type": "ListItem", "position": 1, "name": "Articles", "item": "https://web.dev/articles" },{ "@type": "ListItem", "position": 2, "name": "Script evaluation and long tasks", "item": "https://web.dev/articles/script-evaluation-and-long-tasks" }] } </script> <link rel="stylesheet" href="/extras.css"></head> <body class="" template="page" theme="web-theme" type="article" appearance layout="docs" display-toc pending> <devsite-progress type="indeterminate" id="app-progress"></devsite-progress> <section class="devsite-wrapper"> <devsite-cookie-notification-bar></devsite-cookie-notification-bar><devsite-header role="banner"> <div class="devsite-header--inner nocontent"> <div class="devsite-top-logo-row-wrapper-wrapper"> <div class="devsite-top-logo-row-wrapper"> <div class="devsite-top-logo-row"> <button type="button" id="devsite-hamburger-menu" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Navigation menu button" visually-hidden aria-label="Open menu"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="webDev" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="web.dev"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup.svg" class="devsite-site-logo" alt="web.dev"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> <div class="devsite-top-logo-row-middle"> <div class="devsite-header-upper-tabs"> <devsite-tabs class="upper-tabs"> <nav class="devsite-tabs-wrapper" aria-label="Upper tabs"> <tab > <a href="https://web.dev/about" track-metadata-eventdetail="https://web.dev/about" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - about" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: About" track-name="about" > About </a> </tab> <tab > <a href="https://web.dev/html" track-metadata-eventdetail="https://web.dev/html" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - html" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: HTML" track-name="html" > HTML </a> </tab> <tab > <a href="https://web.dev/css" track-metadata-eventdetail="https://web.dev/css" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - css" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: CSS" track-name="css" > CSS </a> </tab> <tab > <a href="https://web.dev/javascript" track-metadata-eventdetail="https://web.dev/javascript" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - javascript" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: JavaScript" track-name="javascript" > JavaScript </a> </tab> <tab > <a href="https://web.dev/blog" track-metadata-eventdetail="https://web.dev/blog" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - blog" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Blog" track-name="blog" > Blog </a> </tab> <tab > <a href="https://web.dev/learn" track-metadata-eventdetail="https://web.dev/learn" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - learn" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Learn" track-name="learn" > Learn </a> </tab> <tab class="devsite-active"> <a href="https://web.dev/explore" track-metadata-eventdetail="https://web.dev/explore" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - explore" track-metadata-module="primary nav" aria-label="Explore, selected" data-category="Site-Wide Custom Events" data-label="Tab: Explore" track-name="explore" > Explore </a> </tab> <tab > <a href="https://web.dev/patterns" track-metadata-eventdetail="https://web.dev/patterns" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - patterns" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Patterns" track-name="patterns" > Patterns </a> </tab> <tab > <a href="https://web.dev/case-studies" track-metadata-eventdetail="https://web.dev/case-studies" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - case studies" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Case studies" track-name="case studies" > Case studies </a> </tab> </nav> </devsite-tabs> </div> <devsite-search enable-signin enable-search enable-suggestions enable-query-completion project-name="Articles" tenant-name="web.dev" > <form class="devsite-search-form" action="https://web.dev/s/results" method="GET"> <div class="devsite-search-container"> <button type="button" search-open class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Open search"></button> <div class="devsite-searchbox"> <input aria-activedescendant="" aria-autocomplete="list" aria-label="Search" aria-expanded="false" aria-haspopup="listbox" autocomplete="off" class="devsite-search-field devsite-search-query" name="q" placeholder="Search" role="combobox" type="text" value="" > <div class="devsite-search-image material-icons" aria-hidden="true"> </div> <div class="devsite-search-shortcut-icon-container" aria-hidden="true"> <kbd class="devsite-search-shortcut-icon">/</kbd> </div> </div> </div> </form> <button type="button" search-close class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Close search"></button> </devsite-search> </div> <devsite-appearance-selector></devsite-appearance-selector> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> <devsite-user enable-profiles id="devsite-user"> <span class="button devsite-top-button" aria-hidden="true" visually-hidden>Sign in</span> </devsite-user> </div> </div> </div> <div class="devsite-collapsible-section "> <div class="devsite-header-background"> <div class="devsite-product-id-row" > <div class="devsite-product-description-row"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> <a href="https://web.dev/explore" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Lower Header" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="" > Collections </a> </li> </ul> </div> </div> <div class="devsite-doc-set-nav-row"> <devsite-tabs class="lower-tabs"> <nav class="devsite-tabs-wrapper" aria-label="Lower tabs"> <tab > <a href="https://web.dev/explore/learn-core-web-vitals" track-metadata-eventdetail="https://web.dev/explore/learn-core-web-vitals" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - core web vitals" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Core Web Vitals" track-name="core web vitals" > Core Web Vitals </a> </tab> <tab > <a href="https://web.dev/explore/metrics" track-metadata-eventdetail="https://web.dev/explore/metrics" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - metrics" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Metrics" track-name="metrics" > Metrics </a> </tab> <tab > <a href="https://web.dev/explore/fast" track-metadata-eventdetail="https://web.dev/explore/fast" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - fast load times" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Fast load times" track-name="fast load times" > Fast load times </a> </tab> <tab > <a href="https://web.dev/explore/ai" track-metadata-eventdetail="https://web.dev/explore/ai" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - ai" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: AI" track-name="ai" > AI </a> </tab> <tab class="devsite-active"> <a href="https://web.dev/explore/how-to-optimize-inp" track-metadata-eventdetail="https://web.dev/explore/how-to-optimize-inp" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - optimize interaction to next paint (inp)" track-metadata-module="primary nav" aria-label="Optimize Interaction to Next Paint (INP), selected" data-category="Site-Wide Custom Events" data-label="Tab: Optimize Interaction to Next Paint (INP)" track-name="optimize interaction to next paint (inp)" > Optimize Interaction to Next Paint (INP) </a> </tab> <tab > <a href="https://web.dev/explore/progressive-web-apps" track-metadata-eventdetail="https://web.dev/explore/progressive-web-apps" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - progressive web apps" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Progressive Web Apps" track-name="progressive web apps" > Progressive Web Apps </a> </tab> <tab > <a href="https://web.dev/accessibility" track-metadata-eventdetail="https://web.dev/accessibility" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - accessible to all" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Accessible to all" track-name="accessible to all" > Accessible to all </a> </tab> <tab > <a href="https://web.dev/explore/reliable" track-metadata-eventdetail="https://web.dev/explore/reliable" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - network reliability" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Network reliability" track-name="network reliability" > Network reliability </a> </tab> <tab > <a href="https://web.dev/explore/secure" track-metadata-eventdetail="https://web.dev/explore/secure" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - safe and secure" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Safe and secure" track-name="safe and secure" > Safe and secure </a> </tab> <tab > <a href="https://web.dev/explore/discoverable" track-metadata-eventdetail="https://web.dev/explore/discoverable" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - easily discoverable" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Easily discoverable" track-name="easily discoverable" > Easily discoverable </a> </tab> <tab > <a href="https://web.dev/explore/payments" track-metadata-eventdetail="https://web.dev/explore/payments" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - web payments" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Web Payments" track-name="web payments" > Web Payments </a> </tab> <tab > <a href="https://web.dev/explore/media" track-metadata-eventdetail="https://web.dev/explore/media" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - media" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Media" track-name="media" > Media </a> </tab> <tab > <a href="https://web.dev/explore/devices" track-metadata-eventdetail="https://web.dev/explore/devices" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - devices" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Devices" track-name="devices" > Devices </a> </tab> <tab > <a href="https://web.dev/explore/animations" track-metadata-eventdetail="https://web.dev/explore/animations" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - animations" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Animations" track-name="animations" > Animations </a> </tab> <tab > <a href="https://web.dev/explore/identity" track-metadata-eventdetail="https://web.dev/explore/identity" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - identity" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Identity" track-name="identity" > Identity </a> </tab> <tab > <a href="https://web.dev/explore/webassembly" track-metadata-eventdetail="https://web.dev/explore/webassembly" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - webassembly" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: WebAssembly" track-name="webassembly" > WebAssembly </a> </tab> <tab > <a href="https://web.dev/explore/test-automation" track-metadata-eventdetail="https://web.dev/explore/test-automation" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - test automation" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Test automation" track-name="test automation" > Test automation </a> </tab> <tab > <a href="https://web.dev/explore/react" track-metadata-eventdetail="https://web.dev/explore/react" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - react" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: React" track-name="react" > React </a> </tab> <tab > <a href="https://web.dev/explore/angular" track-metadata-eventdetail="https://web.dev/explore/angular" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - angular" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Angular" track-name="angular" > Angular </a> </tab> <tab > <a href="https://web.dev/explore/mini-apps" track-metadata-eventdetail="https://web.dev/explore/mini-apps" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - mini apps" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Mini apps" track-name="mini apps" > Mini apps </a> </tab> </nav> </devsite-tabs> </div> </div> </div> </div> </devsite-header> <devsite-book-nav scrollbars > <div class="devsite-book-nav-filter" hidden> <span class="filter-list-icon material-icons" aria-hidden="true"></span> <input type="text" placeholder="Filter" aria-label="Type to filter" role="searchbox"> <span class="filter-clear-button hidden" data-title="Clear filter" aria-label="Clear filter" role="button" tabindex="0"></span> </div> <nav class="devsite-book-nav devsite-nav nocontent" aria-label="Side menu"> <div class="devsite-mobile-header"> <button type="button" id="devsite-close-nav" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Close navigation" aria-label="Close navigation"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="webDev" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="web.dev"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup.svg" class="devsite-site-logo" alt="web.dev"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> </div> <div class="devsite-book-nav-wrapper"> <div class="devsite-mobile-nav-top"> <ul class="devsite-nav-list"> <li class="devsite-nav-item"> <a href="/about" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: About" track-name="about" data-category="Site-Wide Custom Events" data-label="Responsive Tab: About" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > About </span> </a> </li> <li class="devsite-nav-item"> <a href="/html" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: HTML" track-name="html" data-category="Site-Wide Custom Events" data-label="Responsive Tab: HTML" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > HTML </span> </a> </li> <li class="devsite-nav-item"> <a href="/css" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: CSS" track-name="css" data-category="Site-Wide Custom Events" data-label="Responsive Tab: CSS" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > CSS </span> </a> </li> <li class="devsite-nav-item"> <a href="/javascript" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: JavaScript" track-name="javascript" data-category="Site-Wide Custom Events" data-label="Responsive Tab: JavaScript" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > JavaScript </span> </a> </li> <li class="devsite-nav-item"> <a href="/blog" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Blog" track-name="blog" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Blog" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Blog </span> </a> </li> <li class="devsite-nav-item"> <a href="/learn" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Learn" track-name="learn" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Learn" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Learn </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore" class="devsite-nav-title gc-analytics-event devsite-nav-active" data-category="Site-Wide Custom Events" data-label="Tab: Explore" track-name="explore" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Explore" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Explore </span> </a> <ul class="devsite-nav-responsive-tabs"> <li class="devsite-nav-item"> <a href="/explore/learn-core-web-vitals" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Core Web Vitals" track-name="core web vitals" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Core Web Vitals" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Core Web Vitals </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/metrics" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Metrics" track-name="metrics" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Metrics" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Metrics </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/fast" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Fast load times" track-name="fast load times" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Fast load times" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Fast load times </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/ai" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: AI" track-name="ai" data-category="Site-Wide Custom Events" data-label="Responsive Tab: AI" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > AI </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/how-to-optimize-inp" class="devsite-nav-title gc-analytics-event devsite-nav-has-children devsite-nav-active" data-category="Site-Wide Custom Events" data-label="Tab: Optimize Interaction to Next Paint (INP)" track-name="optimize interaction to next paint (inp)" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Optimize Interaction to Next Paint (INP)" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip menu="_book"> Optimize Interaction to Next Paint (INP) </span> <span class="devsite-nav-icon material-icons" data-icon="forward" menu="_book"> </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/progressive-web-apps" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Progressive Web Apps" track-name="progressive web apps" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Progressive Web Apps" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Progressive Web Apps </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/accessibility" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Accessible to all" track-name="accessible to all" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Accessible to all" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Accessible to all </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/reliable" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Network reliability" track-name="network reliability" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Network reliability" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Network reliability </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/secure" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Safe and secure" track-name="safe and secure" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Safe and secure" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Safe and secure </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/discoverable" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Easily discoverable" track-name="easily discoverable" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Easily discoverable" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Easily discoverable </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/payments" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Web Payments" track-name="web payments" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Web Payments" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Web Payments </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/media" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Media" track-name="media" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Media" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Media </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/devices" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Devices" track-name="devices" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Devices" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Devices </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/animations" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Animations" track-name="animations" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Animations" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Animations </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/identity" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Identity" track-name="identity" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Identity" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Identity </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/webassembly" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: WebAssembly" track-name="webassembly" data-category="Site-Wide Custom Events" data-label="Responsive Tab: WebAssembly" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > WebAssembly </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/test-automation" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Test automation" track-name="test automation" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Test automation" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Test automation </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/react" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: React" track-name="react" data-category="Site-Wide Custom Events" data-label="Responsive Tab: React" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > React </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/angular" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Angular" track-name="angular" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Angular" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Angular </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/mini-apps" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Mini apps" track-name="mini apps" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Mini apps" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Mini apps </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> </ul> </li> <li class="devsite-nav-item"> <a href="/patterns" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Patterns" track-name="patterns" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Patterns" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Patterns </span> </a> </li> <li class="devsite-nav-item"> <a href="/case-studies" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Case studies" track-name="case studies" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Case studies" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Case studies </span> </a> </li> </ul> </div> <div class="devsite-mobile-nav-bottom"> <ul class="devsite-nav-list" menu="_book"> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Familiarize yourself with INP and how to optimize it</span> </div></li> <li class="devsite-nav-item"><a href="/articles/inp" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/inp" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/inp" ><span class="devsite-nav-text" tooltip>Interaction to Next Paint (INP)</span></a></li> <li class="devsite-nav-item"><a href="/articles/optimize-inp" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/optimize-inp" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/optimize-inp" ><span class="devsite-nav-text" tooltip>Optimize Interaction to Next Paint</span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Troubleshooting</span> </div></li> <li class="devsite-nav-item"><a href="/articles/find-slow-interactions-in-the-field" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/find-slow-interactions-in-the-field" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/find-slow-interactions-in-the-field" ><span class="devsite-nav-text" tooltip>Find slow interactions in the field</span></a></li> <li class="devsite-nav-item"><a href="/articles/manually-diagnose-slow-interactions-in-the-lab" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/manually-diagnose-slow-interactions-in-the-lab" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/manually-diagnose-slow-interactions-in-the-lab" ><span class="devsite-nav-text" tooltip>Manually diagnose slow interactions in the lab</span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Optimize poor INP caused by JavaScript</span> </div></li> <li class="devsite-nav-item"><a href="/articles/optimize-long-tasks" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/optimize-long-tasks" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/optimize-long-tasks" ><span class="devsite-nav-text" tooltip>Optimize long tasks</span></a></li> <li class="devsite-nav-item"><a href="/articles/optimize-input-delay" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/optimize-input-delay" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/optimize-input-delay" ><span class="devsite-nav-text" tooltip>Optimize input delay</span></a></li> <li class="devsite-nav-item"><a href="/articles/script-evaluation-and-long-tasks" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/script-evaluation-and-long-tasks" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/script-evaluation-and-long-tasks" ><span class="devsite-nav-text" tooltip>Script evaluation and long tasks</span></a></li> <li class="devsite-nav-item"><a href="/articles/off-main-thread" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/off-main-thread" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/off-main-thread" ><span class="devsite-nav-text" tooltip>Use web workers to run JavaScript off the browser's main thread</span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Optimize poor INP caused by rendering</span> </div></li> <li class="devsite-nav-item"><a href="/articles/avoid-large-complex-layouts-and-layout-thrashing" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/avoid-large-complex-layouts-and-layout-thrashing" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/avoid-large-complex-layouts-and-layout-thrashing" ><span class="devsite-nav-text" tooltip>Avoid large, complex layouts and layout thrashing</span></a></li> <li class="devsite-nav-item"><a href="/articles/reduce-the-scope-and-complexity-of-style-calculations" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/reduce-the-scope-and-complexity-of-style-calculations" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/reduce-the-scope-and-complexity-of-style-calculations" ><span class="devsite-nav-text" tooltip>Reduce the scope and complexity of style calculations</span></a></li> <li class="devsite-nav-item"><a href="/articles/dom-size-and-interactivity" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/dom-size-and-interactivity" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/dom-size-and-interactivity" ><span class="devsite-nav-text" tooltip>How large DOM sizes affect interactivity, and what you can do about it</span></a></li> <li class="devsite-nav-item"><a href="/articles/client-side-rendering-of-html-and-interactivity" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/client-side-rendering-of-html-and-interactivity" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/client-side-rendering-of-html-and-interactivity" ><span class="devsite-nav-text" tooltip>Client-side rendering of HTML and interactivity</span></a></li> </ul> </div> </div> </nav> </devsite-book-nav> <section id="gc-wrapper"> <main role="main" class="devsite-main-content" has-book-nav has-sidebar > <div class="devsite-sidebar"> <div class="devsite-sidebar-content"> <devsite-toc class="devsite-nav" role="navigation" aria-label="On this page" depth="2" scrollbars ></devsite-toc> <devsite-recommendations-sidebar class="nocontent devsite-nav"> </devsite-recommendations-sidebar> </div> </div> <devsite-content> <article class="devsite-article"> <div class="devsite-article-meta nocontent" role="navigation"> <ul class="devsite-breadcrumb-list" aria-label="Breadcrumb"> <li class="devsite-breadcrumb-item "> <a href="https://web.dev/" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="" > Home </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://web.dev/articles" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="2" track-type="globalNav" track-name="breadcrumb" track-metadata-position="2" track-metadata-eventdetail="Articles" > Articles </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://web.dev/explore" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="3" track-type="globalNav" track-name="breadcrumb" track-metadata-position="3" track-metadata-eventdetail="" > Explore </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://web.dev/explore/how-to-optimize-inp" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="4" track-type="globalNav" track-name="breadcrumb" track-metadata-position="4" track-metadata-eventdetail="" > Optimize Interaction to Next Paint (INP) </a> </li> </ul> <devsite-thumb-rating position="header"> </devsite-thumb-rating> </div> <h1 class="devsite-page-title" tabindex="-1"> Script evaluation and long tasks </h1> <devsite-feature-tooltip ack-key="AckCollectionsBookmarkTooltipDismiss" analytics-category="Site-Wide Custom Events" analytics-action-show="Callout Profile displayed" analytics-action-close="Callout Profile dismissed" analytics-label="Create Collection Callout" class="devsite-page-bookmark-tooltip nocontent" dismiss-button="true" id="devsite-collections-dropdown" dismiss-button-text="Dismiss" close-button-text="Got it"> <devsite-bookmark></devsite-bookmark> <span slot="popout-heading"> Stay organized with collections </span> <span slot="popout-contents"> Save and categorize content based on your preferences. </span> </devsite-feature-tooltip> <div class="devsite-page-title-meta"><devsite-view-release-notes></devsite-view-release-notes></div> <devsite-toc class="devsite-nav" depth="2" devsite-toc-embedded > </devsite-toc> <div class="devsite-article-body clearfix "> <p>When loading scripts, it takes time for the browser to evaluate them prior to execution, which can cause long tasks. Learn how script evaluation works, and what you can do to keep it from causing long tasks during page load.</p> <p> <div class="wd-authors" translate="no"> <div class="wd-author"> <img class="devsite-landing-row-item-icon" alt="Jeremy Wagner" src="https://web.dev/images/authors/jlwagner-v3.jpg" decoding="async" height="64" loading="lazy" width="64"> <div> <span> Jeremy Wagner </span> <div class="wd-author__links"> <a href="https://github.com/malchata" aria-label="Jeremy Wagner on GitHub" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 32.6 31.8"> <title>GitHub</title> <path d="M16.3 0C7.3 0 0 7.3 0 16.3c0 7.2 4.7 13.3 11.1 15.5.8.1 1.1-.4 1.1-.8v-2.8c-4.5 1-5.5-2.2-5.5-2.2-.7-1.9-1.8-2.4-1.8-2.4-1.5-1 .1-1 .1-1 1.6.1 2.5 1.7 2.5 1.7 1.5 2.5 3.8 1.8 4.7 1.4.1-1.1.6-1.8 1-2.2-3.6-.4-7.4-1.8-7.4-8.1 0-1.8.6-3.2 1.7-4.4-.1-.3-.7-2 .2-4.2 0 0 1.4-.4 4.5 1.7 1.3-.4 2.7-.5 4.1-.5 1.4 0 2.8.2 4.1.5 3.1-2.1 4.5-1.7 4.5-1.7.9 2.2.3 3.9.2 4.3 1 1.1 1.7 2.6 1.7 4.4 0 6.3-3.8 7.6-7.4 8 .6.5 1.1 1.5 1.1 3V31c0 .4.3.9 1.1.8 6.5-2.2 11.1-8.3 11.1-15.5C32.6 7.3 25.3 0 16.3 0z" fill-rule="evenodd" clip-rule="evenodd" fill="currentColor" /> </svg></a> <a href="https://www.linkedin.com/in/malchata" aria-label="Jeremy Wagner on LinkedIn" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 200 200"> <title>LinkedIn</title> <path d="M185.2 0H14.8C6.6 0 0 6.4 0 14.3v171.3c0 7.9 6.6 14.3 14.8 14.3h170.4c8.1 0 14.8-6.4 14.8-14.3V14.3C199.9 6.4 193.3 0 185.2 0zM60.6 167.3H30.4V77.1h30.2v90.2zM45.5 64.8h-.2c-10.1 0-16.7-6.9-16.7-15.6 0-8.8 6.7-15.6 17.1-15.6 10.3 0 16.7 6.7 16.9 15.6 0 8.6-6.5 15.6-17.1 15.6zm124 102.5h-30.2V119c0-12.1-4.4-20.4-15.3-20.4-8.4 0-13.3 5.6-15.5 11-.8 1.9-1 4.6-1 7.3v50.4H77.3s.4-81.8 0-90.3h30.2v12.8c4-6.1 11.2-14.9 27.2-14.9 19.9 0 34.8 12.9 34.8 40.6v51.8zm-62.2-77.1c0-.1.1-.2.2-.3v.3h-.2z" fill="currentColor" /> </svg></a> <a href="https://jlwagner.net/" aria-label="Jeremy Wagner's homepage" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 30 30"> <title>Homepage</title> <circle cx="14.5" cy="14.5" r="13.5" stroke-width="2" stroke-miterlimit="10" fill="none" stroke="currentColor" /> <ellipse cx="14.5" cy="14.5" rx="6.1" ry="13.5" stroke-width="2" stroke-miterlimit="10" fill="none" stroke="currentColor" /> <path d="M1.6 9.6h25.8M1.6 19.4h25.8" stroke-width="2" stroke-miterlimit="10" fill="none" stroke="currentColor" /> </svg></a> </div> </div> </div> </div></p> <p>When it comes to optimizing <a href="/articles/inp">Interaction to Next Paint (INP)</a>, most of the advice you'll encounter is to optimize interactions themselves. For example, in the <a href="/articles/optimize-long-tasks">optimize long tasks guide</a>, techniques such as yielding with <code translate="no" dir="ltr">setTimeout</code> and others are discussed. These techniques are beneficial, as they allow the main thread some breathing room by avoiding long tasks, which can allow more opportunities for interactions and other activity to run sooner, rather than if they had to wait for a single long task.</p> <p>However, what about the long tasks that come from loading scripts themselves? These tasks can interfere with user interactions and affect a page's INP during load. This guide will explore how browsers handle tasks kicked off by script evaluation, and look into what you may be able to do to break up script evaluation work so that your main thread can be more responsive to user input while the page is loading.</p> <h2 id="what_is_script_evaluation" data-text="What is script evaluation?" tabindex="-1">What is script evaluation?</h2> <p>If you've profiled an application that ships a lot of JavaScript, you may have seen long tasks where the culprit is labeled <strong>Evaluate Script</strong>.</p> <figure> <img src="/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8.png" alt="Script evaluation work as visualized in the performance profiler of Chrome DevTools. The work causes a long task during startup, which blocks the main thread's ability to respond to user interactions." width="697" height="107" srcset="https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_36.png 36w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_48.png 48w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_72.png 72w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_96.png 96w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_480.png 480w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_720.png 720w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_856.png 856w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_960.png 960w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_1440.png 1440w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_1920.png 1920w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/script-evaluation-as-vis-17dff4e25f1c8_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption"> Script evaluation work as shown in the performance profiler in Chrome DevTools. In this case, the work is enough to cause a long task that blocks the main thread from taking on other work—including tasks that drive user interactions. </figcaption> </figure> <p>Script evaluation is a necessary part of executing JavaScript in the browser, as JavaScript is compiled <a href="https://en.wikipedia.org/wiki/Just-in-time_compilation">just-in-time before execution</a>. When a script is evaluated, it is first parsed for errors. If the parser doesn't find errors, the script is then compiled into <a href="https://en.wikipedia.org/wiki/Bytecode">bytecode</a>, and then can continue onto execution.</p> <p>Though necessary, script evaluation can be problematic, as users may try to interact with a page shortly after it initially renders. However, just because a page has <em>rendered</em> doesn't mean that the page has finished <em>loading</em>. Interactions that take place during load can be delayed because the page is busy evaluating scripts. While there's no guarantee that an interaction can take place at this point in time—as a script responsible for it may not have loaded yet—there could be interactions dependent on JavaScript that <em>are</em> ready, or the interactivity doesn't depend on JavaScript at all.</p> <aside class="note"> <b>Note:</b> One metric that might give you insight into whether you have excessive script evaluation occurring during page load is <a href="/articles/tbt">Total Blocking Time (TBT)</a>, as it is a <a href="/articles/user-centric-performance-metrics#types_of_metrics">load responsiveness</a> metric. <a href="https://almanac.httparchive.org/en/2022/performance#inp-and-tbt">Because TBT correlates well with INP</a>, a page with a high TBT is a reasonable indicator that there may be high INP values during load that may be tied to script evaluation work. </aside> <h2 id="the_relationship_between_scripts_and_the_tasks_that_evaluate_them" data-text="The relationship between scripts and the tasks that evaluate them" tabindex="-1">The relationship between scripts and the tasks that evaluate them</h2> <p>How tasks responsible for script evaluation are kicked off depends on whether the script you're loading is loaded with a typical <code translate="no" dir="ltr"><script></code> element, or if the script is a module loaded with the <code translate="no" dir="ltr">type=module</code>. Since browsers have the tendency to handle things differently, how the major browser engines handle script evaluation will be touched upon where script evaluation behaviors across them vary.</p> <h3 id="scripts_loaded_with_the_script_element" data-text="Scripts loaded with the <script> element" tabindex="-1">Scripts loaded with the <code translate="no" dir="ltr"><script></code> element</h3> <aside class="key-point"> <b>Important:</b> this section deals with loading scripts using the <code translate="no" dir="ltr"><script></code> element <em>without</em> the <code translate="no" dir="ltr">type=module</code> attribute. </aside> <p>The number of tasks dispatched to evaluate scripts generally has a direct relationship with the number of <code translate="no" dir="ltr"><script></code> elements on a page. Each <code translate="no" dir="ltr"><script></code> element kicks off a task to evaluate the requested script so it can be parsed, compiled, and executed. <strong>This is the case for Chromium-based browsers, Safari, <em>and</em> Firefox.</strong></p> <p>Why does this matter? Say you're using a bundler to manage your production scripts, and you've configured it to bundle everything your page needs to run into a single script. If this is the case for your website, you can expect that there will be a single task dispatched to evaluate that script. Is this a bad thing? Not necessarily—unless that script is <em>huge</em>.</p> <p>You can break up script evaluation work by avoiding loading large chunks of JavaScript, and load more individual, smaller scripts using additional <code translate="no" dir="ltr"><script></code> elements.</p> <aside class="key-point"><b>Key point: </b> As devices vary in their capability, it's very difficult to define a set limit for how large individual scripts should be. To achieve a decent balance between compression efficiency, download time, and script evaluation time, a limit of 100 kilobytes per individual script is a good target. </aside> <p>While you should always strive to load as little JavaScript as possible during page load, splitting up your scripts ensures that, instead of one large task that may block the main thread, you have a greater number of smaller tasks that won't block the main thread at all—or at least less than what you started with.</p> <figure> <img src="/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a.png" alt="Multiple tasks involving script evaluation as visualized in the performance profiler of Chrome DevTools. Because multiple smaller scripts are loaded instead of fewer larger scripts, tasks are less likely to become long tasks, allowing the main thread to respond to user input more quickly." width="800" height="174" srcset="https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_36.png 36w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_48.png 48w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_72.png 72w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_96.png 96w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_480.png 480w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_720.png 720w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_856.png 856w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_960.png 960w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_1440.png 1440w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_1920.png 1920w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/multiple-tasks-involving-744bb86f09b1a_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption"> Multiple tasks spawned to evaluate scripts as a result of multiple <code translate="no" dir="ltr"><script></code> elements present in the page's HTML. This is preferable to sending one large script bundle to users, which is more likely to block the main thread. </figcaption> </figure> <p>You can think of breaking up tasks for script evaluation as being somewhat similar to <a href="/articles/optimize-long-tasks#use_asyncawait_to_create_yield_points">yielding during event callbacks that run during an interaction</a>. However, with script evaluation, the yielding mechanism breaks up the JavaScript you load into multiple smaller scripts, rather than a smaller number of larger scripts than are more likely to block the main thread.</p> <aside class="key-point"> <b>Key point:</b> Chromium-based browsers will execute all loaded scripts with the <code translate="no" dir="ltr">defer</code> attribute in the same task as the <a href="https://developer.mozilla.org/docs/Web/API/Window/DOMContentLoaded_event"><code translate="no" dir="ltr">DOMContentLoaded</code> event</a>. While this minimizes overall layout work, the cost is the increased likelihood of a longer task, which may cause other performance problems. A potential solution is <a href="https://github.com/whatwg/html/issues/6230">being explored</a>. This behavior also takes place with scripts loaded using the <code translate="no" dir="ltr">type=module</code> attribute, as such scripts are deferred by default. </aside> <h3 id="scripts_loaded_with_the_script_element_and_the_typemodule_attribute" data-text="Scripts loaded with the <script> element and the type=module attribute" tabindex="-1">Scripts loaded with the <code translate="no" dir="ltr"><script></code> element and the <code translate="no" dir="ltr">type=module</code> attribute</h3> <aside class="key-point"><b>Key point: </b> If you're not bundling ES modules and loading them using the <code translate="no" dir="ltr">type=module</code> attribute, you could actually end up slowing down page startup. For more information, read the <a href="/articles/script-evaluation-and-long-tasks#trade-offs_and_considerations">trade-offs and considerations section</a> later on in this guide. </aside> <p>It's now possible to load ES modules natively in the browser with the <a href="/articles/serve-modern-code-to-modern-browsers#use_lessscript_type=modulegreater"><code translate="no" dir="ltr">type=module</code> attribute</a> on the <code translate="no" dir="ltr"><script></code> element. This approach to script loading carries some developer experience benefits, such as not having to transform code for production use—especially when used in combination with <a href="https://developer.mozilla.org/docs/Web/HTML/Element/script/type/importmap">import maps</a>. However, loading scripts in this way schedules tasks that differ from browser to browser.</p> <h4 id="chromium-based_browsers" data-text="Chromium-based browsers" tabindex="-1">Chromium-based browsers</h4> <p>In browsers such as Chrome—or those derived from it—loading ES modules using the <code translate="no" dir="ltr">type=module</code> attribute produces different sorts of tasks than you'd normally see when not using <code translate="no" dir="ltr">type=module</code>. For example, a task for each module script will run that involves activity labeled as <strong>Compile module</strong>.</p> <figure> <img src="/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df.png" alt="Module compilation work in multiple tasks as visualized in Chrome DevTools." width="800" height="140" srcset="https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_36.png 36w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_48.png 48w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_72.png 72w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_96.png 96w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_480.png 480w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_720.png 720w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_856.png 856w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_960.png 960w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_1440.png 1440w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_1920.png 1920w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/module-compilation-in-mu-724bec674b5df_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption"> Module loading behavior in Chromium-based browsers. Each module script will spawn a <strong>Compile module</strong> call to compile their contents prior to evaluation. </figcaption> </figure> <p>Once the modules have compiled, any code that subsequently runs in them will kick off activity labeled as <strong>Evaluate module</strong>.</p> <figure> <img src="/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916.png" alt="Just-in-time evaluation of a module as visualized in the performance panel of Chrome DevTools." width="800" height="184" srcset="https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_36.png 36w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_48.png 48w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_72.png 72w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_96.png 96w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_480.png 480w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_720.png 720w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_856.png 856w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_960.png 960w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_1440.png 1440w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_1920.png 1920w,https://web.dev/static/articles/script-evaluation-and-long-tasks/image/just-time-evaluation-a-660d5e72fb916_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption"> When code in a module runs, that module will be evaluated just-in-time. </figcaption> </figure> <p>The effect here—in Chrome and related browsers, at least—is that the compilation steps are broken up when using ES modules. This is a clear win in terms of managing long tasks; however, the resulting module evaluation work that results still means you're incurring some unavoidable cost. While you should strive to ship as little JavaScript as possible, using ES modules—regardless of the browser—provides the following benefits:</p> <ul> <li>All module code is automatically run in <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Strict_mode">strict mode</a>, which allows potential optimizations by JavaScript engines that couldn't otherwise be made in a non-strict context.</li> <li>Scripts loaded using <code translate="no" dir="ltr">type=module</code> are treated as if they were <a href="https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-defer">deferred</a> by default. It's possible to use the <code translate="no" dir="ltr">async</code> attribute on scripts loaded with <code translate="no" dir="ltr">type=module</code> to change this behavior.</li> </ul> <h4 id="safari_and_firefox" data-text="Safari and Firefox" tabindex="-1">Safari and Firefox</h4> <p>When modules are loaded in Safari and Firefox, each of them is evaluated in a separate task. This means you could theoretically load a single top-level module consisting of only <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import">static <code translate="no" dir="ltr">import</code></a> statements to other modules, and every module loaded will incur a separate network request and task to evaluate it.</p> <h3 id="scripts_loaded_with_dynamic_import" data-text="Scripts loaded with dynamic import()" tabindex="-1">Scripts loaded with dynamic <code translate="no" dir="ltr">import()</code></h3> <p><a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/import">Dynamic <code translate="no" dir="ltr">import()</code></a> is another method for loading scripts. Unlike static <code translate="no" dir="ltr">import</code> statements that are required to be at the top of an ES module, a dynamic <code translate="no" dir="ltr">import()</code> call can appear anywhere in a script to load a chunk of JavaScript on demand. This technique is called <a href="/articles/reduce-javascript-payloads-with-code-splitting">code splitting</a>.</p> <p>Dynamic <code translate="no" dir="ltr">import()</code> has two advantages when it comes to improving INP:</p> <ol> <li>Modules which are deferred to load later reduce main thread contention during startup by reducing the amount of JavaScript loaded at that time. This frees up the main thread so it can be more responsive to user interactions.</li> <li>When dynamic <code translate="no" dir="ltr">import()</code> calls are made, each call will effectively separate the compilation and evaluation of each module to its own task. Of course, a dynamic <code translate="no" dir="ltr">import()</code> that loads a very large module will kick off a rather large script evaluation task, and that can interfere with the ability of the main thread to respond to user input if the interaction occurs at the same time as the dynamic <code translate="no" dir="ltr">import()</code> call. <strong>Therefore, it's still very important that you load as little JavaScript as possible.</strong></li> </ol> <p>Dynamic <code translate="no" dir="ltr">import()</code> calls behave similarly in all major browser engines: the script evaluation tasks that result will be the same as the amount of modules that are dynamically imported.</p> <h3 id="scripts_loaded_in_a_web_worker" data-text="Scripts loaded in a web worker" tabindex="-1">Scripts loaded in a web worker</h3> <p><a href="https://developer.mozilla.org/docs/Web/API/Web_Workers_API">Web workers</a> are a special JavaScript use case. Web workers are registered on the main thread, and the code within the worker then runs on its own thread. This is hugely beneficial in the sense that—while the code that registers the web worker runs on the main thread—the code within the web worker doesn't. This reduces main thread congestion, and can help keep the main thread more responsive to user interactions.</p> <p>In addition to reducing main thread work, web workers <em>themselves</em> can load external scripts to be used in the worker context, either through <a href="https://developer.mozilla.org/docs/Web/API/WorkerGlobalScope/importScripts"><code translate="no" dir="ltr">importScripts</code></a> or static <code translate="no" dir="ltr">import</code> statements in browsers that support <a href="/articles/module-workers">module workers</a>. The result is that any script requested by a web worker is evaluated off the main thread.</p> <aside class="objective"><strong>Read to learn more:</strong> <a href="/articles/off-main-thread">Use web workers to run JavaScript off the browser's main thread</a>. </aside> <h2 id="trade-offs_and_considerations" data-text="Trade-offs and considerations" tabindex="-1">Trade-offs and considerations</h2> <p>While breaking up your scripts into separate, smaller files helps limit long tasks as opposed to loading fewer, much larger files, it's important to take some things into account when deciding how to break scripts up.</p> <h3 id="compression_efficiency" data-text="Compression efficiency" tabindex="-1">Compression efficiency</h3> <p><a href="/articles/reduce-network-payloads-using-text-compression">Compression</a> is a factor when it comes to breaking up scripts. When scripts are smaller, compression becomes somewhat less efficient. Larger scripts will benefit much more from compression. While increasing compression efficiency helps to keep load times for scripts as low as possible, it's a bit of a balancing act to ensure that you're breaking up scripts into enough smaller chunks to facilitate better interactivity during startup.</p> <p>Bundlers are ideal tools for managing the output size for the scripts your website depends on:</p> <ul> <li>Where webpack is concerned, its <code translate="no" dir="ltr">SplitChunksPlugin</code> plugin can help. Consult the <a href="https://webpack.js.org/plugins/split-chunks-plugin/"><code translate="no" dir="ltr">SplitChunksPlugin</code> documentation</a> for options you can set to help manage asset sizes.</li> <li>For other bundlers such as <a href="https://rollupjs.org/">Rollup</a> and <a href="https://esbuild.github.io/">esbuild</a>, you can manage script file sizes by using dynamic <code translate="no" dir="ltr">import()</code> calls in your code. These bundlers—as well as webpack—will automatically break off the dynamically imported asset into its own file, thus avoiding larger initial bundle sizes.</li> </ul> <h3 id="cache_invalidation" data-text="Cache invalidation" tabindex="-1">Cache invalidation</h3> <p>Cache invalidation plays a big role in how fast a page loads on repeat visits. When you ship large, monolithic script bundles, you're at a disadvantage when it comes to browser caching. This is because when you update your first-party code—either through updating packages or shipping bug fixes—the entire bundle becomes invalidated and must be downloaded again.</p> <p>By breaking up your scripts, you're not just breaking up script evaluation work across smaller tasks, you're also increasing the likelihood that return visitors will grab more scripts from the browser cache instead of from the network. This translates into an overall faster page load.</p> <aside class="key-point"><b>Key point: </b> In order for caching to be both efficient and to avoid stale resources from being served from the cache, be sure that your bundler is <a href="https://bundlers.tooling.report/hashing/">generating resources with a hash in the filename</a>. </aside> <h3 id="nested_modules_and_loading_performance" data-text="Nested modules and loading performance" tabindex="-1">Nested modules and loading performance</h3> <p>If you're shipping ES modules in production and loading them with the <code translate="no" dir="ltr">type=module</code> attribute, you need to be aware of how module nesting can impact startup time. Module nesting refers to when an ES module statically imports another ES module that statically imports another ES module:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="JavaScript"><code translate="no" dir="ltr"><span class="devsite-syntax-c1">// a.js</span> <span class="devsite-syntax-k">import</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-nx">b</span><span class="devsite-syntax-p">}</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kr">from</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'./b.js'</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-c1">// b.js</span> <span class="devsite-syntax-k">import</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span><span class="devsite-syntax-nx">c</span><span class="devsite-syntax-p">}</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kr">from</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'./c.js'</span><span class="devsite-syntax-p">;</span> </code></pre></devsite-code> <p>If your ES modules are not bundled together, the preceding code results in a network request chain: when <code translate="no" dir="ltr">a.js</code> is requested from a <code translate="no" dir="ltr"><script></code> element, another network request is dispatched for <code translate="no" dir="ltr">b.js</code>, which then involves <em>another</em> request for <code translate="no" dir="ltr">c.js</code>. One way to avoid this is to use a bundler—but be sure you're configuring your bundler to break up scripts to spread out script evaluation work.</p> <p>If you don't want to use a bundler, then another way to get around nested module calls is to use the <a href="https://developer.chrome.com/blog/modulepreload"><code translate="no" dir="ltr">modulepreload</code> resource hint</a>, which will preload ES modules ahead of time to avoid network request chains.</p> <h2 id="conclusion" data-text="Conclusion" tabindex="-1">Conclusion</h2> <p>Optimizing evaluation of scripts in the browser is no doubt a tricky feat. The approach depends on your website's requirements and constraints. However, by splitting up scripts, you're spreading the work of script evaluation over numerous smaller tasks, and therefore giving the main thread the ability to handle user interactions more efficiently, rather than blocking the main thread.</p> <p>To recap, here are some things you can to do to break up large script evaluation tasks:</p> <ul> <li>When loading scripts using the <code translate="no" dir="ltr"><script></code> element without the <code translate="no" dir="ltr">type=module</code> attribute, avoid loading scripts that are very large, as these will kick off resource-intensive script evaluation tasks that block the main thread. Spread out your scripts over more <code translate="no" dir="ltr"><script></code> elements to break up this work.</li> <li>Using the <code translate="no" dir="ltr">type=module</code> attribute to load ES modules natively in the browser will kick off individual tasks for evaluation for each separate module script.</li> <li>Reduce the size of your initial bundles by using dynamic <code translate="no" dir="ltr">import()</code> calls. This also works in bundlers, as bundlers will treat each dynamically imported module as a "split point," resulting in a separate script being generated for each dynamically imported module.</li> <li>Be sure to weigh trade-offs such as compression efficiency and cache invalidation. Larger scripts will compress better, but are more likely to involve more expensive script evaluation work in fewer tasks, and result in browser cache invalidation, leading to overall lower caching efficiency.</li> <li>If using ES modules natively without bundling, use the <code translate="no" dir="ltr">modulepreload</code> resource hint to optimize the loading of them during startup.</li> <li>As always, ship as little JavaScript as possible.</li> </ul> <p>It's a balancing act for sure—but by breaking up scripts and reducing initial payloads with dynamic <code translate="no" dir="ltr">import()</code>, you can achieve better startup performance and better accommodate user interactions during that crucial startup period. This should help you score better on the INP metric, thus delivering a better user experience.</p> </div> <devsite-thumb-rating position="footer"> </devsite-thumb-rating> <div class="devsite-floating-action-buttons"> </div> </article> <devsite-content-footer class="nocontent"> <p>Except as otherwise noted, the content of this page is licensed under the <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 License</a>, and code samples are licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>. For details, see the <a href="https://developers.google.com/site-policies">Google Developers Site Policies</a>. Java is a registered trademark of Oracle and/or its affiliates.</p> <p>Last updated 2023-05-09 UTC.</p> </devsite-content-footer> <devsite-notification > </devsite-notification> <div class="devsite-content-data"> <template class="devsite-content-data-template"> [[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2023-05-09 UTC."],[],[]] </template> </div> </devsite-content> </main> <devsite-footer-promos class="devsite-footer"> </devsite-footer-promos> <devsite-footer-linkboxes class="devsite-footer"> <nav class="devsite-footer-linkboxes nocontent" aria-label="Footer links"> <ul class="devsite-footer-linkboxes-list"> <li class="devsite-footer-linkbox wd-footer-promo"> <h3 class="devsite-footer-linkbox-heading no-link">web.dev</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <h3 class="devsite-footer-linkbox-heading no-link"> web.dev </h3> <div class="devsite-footer-linkbox-description">We want to help you build beautiful, accessible, fast, and secure websites that work cross-browser, and for all of your users. This site is our home for content to help you on that journey, written by members of the Chrome team, and external experts.</div> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Contribute</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://issuetracker.google.com/issues/new?component=1400680&template=1857359" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > File a bug </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://issuetracker.google.com/issues?q=status:open%20componentid:1400680&s=created_time:desc" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > See open issues </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Related Content</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://developer.chrome.com/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Chrome for Developers </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://blog.chromium.org/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Chromium updates </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/case-studies" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Case studies </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/shows" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Podcasts & shows </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Follow</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://twitter.com/ChromiumDev" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > @ChromiumDev on X </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://www.youtube.com/user/ChromeDevelopers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > YouTube </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://www.linkedin.com/showcase/chrome-for-developers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Chrome for Developers on LinkedIn </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/static/blog/feed.xml" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > RSS </a> </li> </ul> </li> </ul> </nav> </devsite-footer-linkboxes> <devsite-footer-utility class="devsite-footer"> <div class="devsite-footer-utility nocontent"> <nav class="devsite-footer-utility-links" aria-label="Utility links"> <ul class="devsite-footer-utility-list"> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/terms" data-category="Site-Wide Custom Events" data-label="Footer Terms link" > Terms </a> </li> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/privacy" data-category="Site-Wide Custom Events" data-label="Footer Privacy link" > Privacy </a> </li> <li class="devsite-footer-utility-item glue-cookie-notification-bar-control"> <a class="devsite-footer-utility-link gc-analytics-event" href="#" data-category="Site-Wide Custom Events" data-label="Footer Manage cookies link" aria-hidden="true" > Manage cookies </a> </li> </ul> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> </nav> </div> </devsite-footer-utility> <devsite-panel></devsite-panel> </section></section> <devsite-sitemask></devsite-sitemask> <devsite-snackbar></devsite-snackbar> <devsite-tooltip ></devsite-tooltip> <devsite-heading-link></devsite-heading-link> <devsite-analytics> <script type="application/json" analytics>[]</script> <script type="application/json" tag-management>{"at": "True", "ga4": [], "ga4p": [], "gtm": [{"id": "GTM-MZWCJPP", "purpose": 0}], "parameters": {"internalUser": "False", "language": {"machineTranslated": "False", "requested": "en", "served": "en"}, "pageType": "article", "projectName": "Articles", "signedIn": "False", "tenant": "web", "recommendations": {"sourcePage": "", "sourceType": 0, "sourceRank": 0, "sourceIdenticalDescriptions": 0, "sourceTitleWords": 0, "sourceDescriptionWords": 0, "experiment": ""}, "experiment": {"ids": ""}}}</script> </devsite-analytics> <devsite-badger></devsite-badger> <script nonce="aJ69EPq0yBaNITkzKAW1pGNCxgHsH0"> (function(d,e,v,s,i,t,E){d['GoogleDevelopersObject']=i; t=e.createElement(v);t.async=1;t.src=s;E=e.getElementsByTagName(v)[0]; E.parentNode.insertBefore(t,E);})(window, document, 'script', 'https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/js/app_loader.js', '[27,"en",null,"/js/devsite_app_module.js","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web","https://web-dot-devsite-v2-prod-3p.appspot.com",1,null,["/_pwa/web/manifest.json","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/images/video-placeholder.svg","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/favicon.png","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup.svg","https://fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"],1,null,[1,6,8,12,14,17,21,25,50,52,63,70,75,76,80,87,91,92,93,97,98,100,101,102,103,104,105,107,108,109,110,112,113,116,117,118,120,122,124,125,126,127,129,130,131,132,133,134,135,136,138,140,141,147,148,149,151,152,156,157,158,159,161,163,164,168,169,170,179,180,182,183,186,191,193,196],"AIzaSyCNm9YxQumEXwGJgTDjxoxXK6m1F-9720Q","AIzaSyCc76DZePGtoyUjqKrLdsMGk_ry7sljLbY","web.dev","AIzaSyB9bqgQ2t11WJsOX8qNsCQ6U-w91mmqF-I","AIzaSyAdYnStPdzjcJJtQ0mvIaeaMKj7_t6J_Fg",null,null,null,["MiscFeatureFlags__enable_project_variables","TpcFeatures__enable_required_headers","Cloud__enable_llm_concierge_chat","BookNav__enable_tenant_cache_key","Profiles__enable_developer_profiles_callout","DevPro__enable_developer_subscriptions","OnSwitch__enable","Profiles__enable_recognition_badges","Search__enable_ai_eligibility_checks","MiscFeatureFlags__developers_footer_image","TpcFeatures__enable_mirror_tenant_redirects","Profiles__require_profile_eligibility_for_signin","MiscFeatureFlags__enable_variable_operator","DevPro__enable_cloud_innovators_plus","Profiles__enable_page_saving","MiscFeatureFlags__enable_explain_this_code","Experiments__reqs_query_experiments","Search__enable_page_map","MiscFeatureFlags__developers_footer_dark_image","Profiles__enable_completecodelab_endpoint","Concierge__enable_pushui","Profiles__enable_complete_playlist_endpoint","Cloud__enable_cloud_dlp_service","Profiles__enable_release_notes_notifications","Analytics__enable_clearcut_logging","Profiles__enable_profile_collections","Cloud__enable_free_trial_server_call","CloudShell__cloud_code_overflow_menu","Cloud__enable_cloudx_ping","Cloud__enable_cloudx_experiment_ids","Profiles__enable_dashboard_curated_recommendations","MiscFeatureFlags__emergency_css","Cloud__enable_cloud_shell","Search__enable_dynamic_content_confidential_banner","EngEduTelemetry__enable_engedu_telemetry","Profiles__enable_public_developer_profiles","Cloud__enable_cloud_facet_chat","Search__enable_suggestions_from_borg","MiscFeatureFlags__enable_firebase_utm","CloudShell__cloud_shell_button","Cloud__enable_cloud_shell_fte_user_flow","Profiles__enable_awarding_url","Cloud__enable_legacy_calculator_redirect","MiscFeatureFlags__enable_view_transitions"],null,null,"AIzaSyA58TaKli1DculwmAmbpzLVGuWc8eCQgQc","https://developerscontentserving-pa.googleapis.com","AIzaSyDWBU60w0P9hEkr29kkksYs8Z7gvZ8u_wc","https://developerscontentsearch-pa.googleapis.com",2,4,null,"https://developerprofiles-pa.googleapis.com",[27,"web","web.dev","web.dev",null,"web-dot-devsite-v2-prod-3p.appspot.com",null,null,[null,null,null,null,null,null,null,null,null,null,null,[1],null,null,null,null,null,null,[1],null,null,null,null,[1,null,1],[1,1,null,1,1]],null,[38,null,null,null,null,null,"/images/lockup.svg","/images/touchicon-180.png",null,null,null,1,1,null,null,null,null,null,null,null,null,2,null,null,null,"/images/lockup-dark-theme.svg",[]],[],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[[],[1,1]],[[null,null,null,null,null,["GTM-MZWCJPP"],null,null,null,null,null,[["GTM-MZWCJPP",1]],1]],null,4]]') </script> <devsite-a11y-announce></devsite-a11y-announce> </body> </html>