HTML performance basics: lazy loading, preload, and script loading
Performance is largely treated as a CSS and JavaScript concern, but HTML itself has real, direct control over resource loading order and priority — the attributes covered in this part, tying directly back to the Core Web Vitals concerns the SEO series covers from a strategy angle.
loading="lazy": deferring what isn't immediately visible
<img src="/images/roasting-step-3.jpg" alt="..." loading="lazy" />
<iframe src="https://www.youtube.com/embed/..." title="..." loading="lazy"></iframe>Covered in parts 6 and 13 individually — worth restating together here as the same real principle applied to two different element types: don't load a real resource until it's genuinely about to be needed. The one real exception, covered next, is content visible the instant the page loads.
Never lazy-load what's immediately visible
<!-- wrong: this image is the first thing a visitor sees — lazy-loading
it only delays what should load fastest -->
<img src="/images/hero-coffee-pour.jpg" alt="..." loading="lazy" />
<!-- correct: eager (the default) for above-the-fold content -->
<img src="/images/hero-coffee-pour.jpg" alt="..." loading="eager" />loading="lazy" on an image that's visible the moment the page loads (a hero image, a logo) actively delays it — the browser has to first determine the image is "about to scroll into view," which for an already-visible image is immediate but still adds a real, measurable delay compared to loading it eagerly from the start. This directly affects Largest Contentful Paint, a real Core Web Vitals metric — lazy-loading the single largest above-the-fold image is a genuinely common, real mistake that actively hurts the exact metric someone was likely trying to improve.
<link rel="preload">: telling the browser what matters most, early
<head>
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/images/hero-coffee-pour.jpg" as="image" />
</head>A browser discovers most resources by parsing the HTML top to bottom and encountering references to them along the way — a background image referenced only inside a CSS file, for instance, isn't discovered until that CSS file itself has been fetched and parsed, a real, measurable delay. preload tells the browser about a genuinely critical resource immediately, from the <head>, letting it start fetching in parallel rather than waiting to discover it later in the normal loading sequence — worth reserving specifically for resources that are both critical and not otherwise discovered early, like a custom font or a real hero image, not applied broadly to every resource on the page.
async and defer: the real difference for scripts
<script src="/js/analytics.js" async></script>
<script src="/js/main.js" defer></script>No attribute: the browser stops parsing the rest of the HTML entirely
until the script downloads and finishes executing — a real,
significant blocking delay
async: downloads in parallel with HTML parsing, then pauses parsing
to execute the instant it's ready — execution order relative to
other async scripts isn't guaranteed
defer: downloads in parallel with HTML parsing too, but execution is
held until parsing finishes, in the real, original document orderA real, practical rule of thumb: async for scripts that don't depend on the page's DOM or on other scripts (an analytics snippet, similar in spirit to this site's own deferred GTM loading approach); defer for scripts that need the full DOM to exist first, or that need to run in a specific real order relative to other scripts — which covers most genuine application logic.
A <script> tag with neither async nor defer, placed in <head>, is one of the most common real causes of a slow-feeling page — the browser stops doing anything else, including rendering visible content, until that script finishes. This is exactly why a script tag with no loading attribute has traditionally been placed at the very end of <body> instead of in <head> — a real, older workaround for a problem async/defer now solve more precisely.
Resource hints: preconnect and dns-prefetch
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />For real, known third-party origins a page will definitely request something from (a font host, an analytics domain), preconnect lets the browser establish the real DNS lookup, TCP connection, and TLS handshake ahead of time, in parallel with everything else — so the actual resource request, when it happens, skips straight to the data transfer instead of paying that full connection setup cost at that moment. dns-prefetch is a lighter, more limited version doing just the DNS lookup — worth using as a fallback for older browsers that don't support preconnect.
Next: common HTML mistakes — a direct, honest roundup of the real failure patterns this series has flagged individually, brought together in one place.