HTMLPerformance Best Practices

HTML Performance Best Practices

Performance isn't only a JavaScript or CSS concern—a lot of it comes down to decisions made directly in your HTML markup: how deep your DOM is, which resources you hint the browser to prioritize, which images load lazily, and whether elements have the dimensions they need to avoid shifting the page around. This page is a practical checklist.

Minimize DOM Depth and Size
Every extra wrapper <div> adds a node the browser must parse, style, lay out, and keep in memory. A large or deeply nested DOM slows down style recalculation, layout, and any JavaScript that queries or walks it.

dom-depth.html

HTML
<!-- Bloated: five wrapper divs for one line of text -->
<div class="wrapper">
  <div class="container">
    <div class="row">
      <div class="col">
        <div class="inner">
          <p>Hello</p>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- Flatter: same visual result, far fewer nodes -->
<section class="card">
  <p>Hello</p>
</section>
  • Aim for well under 1,500 total DOM nodes on any one page where possible.

  • Avoid nesting deeper than ~10–15 levels; deep trees slow style and layout recalculation.

  • Use CSS Grid/Flexbox to achieve layouts instead of stacking wrapper divs.

Lazy-Load Below-the-Fold Images
Add loading="lazy" to every <img> and <iframe> that isn't visible without scrolling. It defers the network request until the user actually scrolls near it, for zero JavaScript cost. See the dedicated lazy-loading page for details on when to skip it.

lazy-below-fold.html

HTML
<img src="footer-illustration.svg" alt="" loading="lazy" width="600" height="300" />
Defer Non-Critical Scripts
Add defer to application scripts and async to independent third-party scripts (analytics, ads) so neither blocks HTML parsing. See the dedicated async/defer page for the full comparison.

defer-scripts.html

HTML
<script src="/js/app.js" defer></script>
<script src="https://analytics.example.com/tag.js" async></script>
Preload Critical Resources
<link rel="preload"> tells the browser to start fetching a resource immediately, at high priority, without waiting for the parser or CSSOM to discover it naturally. Use it for resources the browser wouldn't otherwise find early—a hero image referenced only in CSS, or a custom font.

preload.html

HTML
<head>
  <link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin />
  <link rel="preload" href="/images/hero.avif" as="image" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
</head>
Preload sparingly
Preloading everything defeats the purpose—it competes with the resources that actually need bandwidth first. Reserve it for the one or two resources that are truly critical to the first paint.
Use Modern Image Formats
WebP and AVIF produce dramatically smaller files than JPEG or PNG at equivalent visual quality. Serve them with a <picture> fallback chain so older browsers still get a working image.

modern-formats.html

HTML
<picture>
  <source srcset="/images/product.avif" type="image/avif" />
  <source srcset="/images/product.webp" type="image/webp" />
  <img src="/images/product.jpg" alt="Wireless headphones" width="640" height="480" />
</picture>
Avoid Layout-Shift-Causing Patterns

Cumulative Layout Shift (CLS) measures how much visible content jumps around after it first renders. The most common HTML-level cause is an image, iframe, or ad slot that has no reserved space, so the surrounding content jumps down once it loads.

missing-dimensions.html

HTML
<!-- Bad: no dimensions, causes a layout jump when the image loads -->
<img src="banner.jpg" alt="Sale banner" />

<!-- Good: browser reserves the exact space up front -->
<img src="banner.jpg" alt="Sale banner" width="1200" height="400" />
width/height still work with responsive CSS
Setting width and height attributes doesn't lock an image to that pixel size—combined with img { width: 100%; height: auto; } in your CSS, the attributes only establish the correct aspect ratio for layout reservation, while the image still scales responsively.
  • Always set width and height (or aspect-ratio in CSS) on images, videos, and iframes.

  • Reserve space for ad slots and embeds before they load, even if the exact size varies.

  • Avoid inserting content above existing content unless in response to a user interaction.

  • Use font-display: swap (or optional) and preload key fonts to reduce font-swap layout shift.

Full Checklist

Practice

Why it matters

Minimize DOM depth/size

Faster style calculation, layout, and JS traversal

loading="lazy" on below-fold media

Reduces initial network and CPU load

defer/async on scripts

Stops JS from blocking HTML parsing

rel="preload" on critical assets

Starts fetching key resources sooner

Modern image formats (WebP/AVIF)

Smaller file sizes at equal quality

width/height on media

Prevents layout shift (CLS)

Minified HTML/CSS/JS

Fewer bytes to transfer and parse

Small changes, compounding wins
None of these require a framework or build-tool change—they're attributes and tags you can add directly to your markup today, and together they meaningfully move Core Web Vitals scores.
Measure, don't just apply
Run Lighthouse or PageSpeed Insights before and after making these changes. Some optimizations matter far more on one page than another—prioritize based on real data.