Best Practices
HTML Performance Optimization
Optimize HTML for faster page loads and better user experience. Learn techniques for reducing page weight, improving render speed, and maximizing web performance.
Why Performance Matters
53%
of users abandon sites that take > 3 seconds to load
1 second
delay = 7% reduction in conversions
100ms
faster = 1% increase in revenue (Amazon)
Top 3
Google ranking factor for search
Critical Rendering Path
Understanding how browsers render HTML is key to optimization:
- Parse HTML → Build DOM tree
- Parse CSS → Build CSSOM tree
- Combine DOM + CSSOM → Render tree
- Layout → Calculate positions
- Paint → Draw pixels
Goal: Minimize blocking resources and reduce time to first paint.
Optimize HTML Document Structure
✅ Optimized HTML Template
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Essential meta tags first -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Preconnect to external domains -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">
<!-- Preload critical resources -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.jpg" as="image">
<!-- Critical CSS inline -->
<style>
/* Above-the-fold CSS here */
body { margin: 0; font-family: sans-serif; }
.hero { height: 100vh; background: #333; }
</style>
<title>Page Title</title>
<!-- Defer non-critical CSS -->
<link rel="stylesheet" href="/css/main.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/css/main.css"></noscript>
</head>
<body>
<!-- Content here -->
<!-- Scripts at end with defer/async -->
<script src="/js/main.js" defer></script>
<script src="/js/analytics.js" async></script>
</body>
</html>Optimize Images
Lazy Loading
HTML
<!-- Native lazy loading (modern browsers) -->
<img src="image.jpg"
alt="Description"
loading="lazy"
width="800"
height="600">
<!-- Load hero image immediately -->
<img src="hero.jpg"
alt="Hero"
loading="eager"
fetchpriority="high">
<!-- Lazy load all below-the-fold images -->
<img src="photo1.jpg" alt="Photo 1" loading="lazy">
<img src="photo2.jpg" alt="Photo 2" loading="lazy">
<img src="photo3.jpg" alt="Photo 3" loading="lazy">Responsive Images (srcset)
HTML
<!-- Serve appropriate image size -->
<img src="image-400w.jpg"
srcset="image-400w.jpg 400w,
image-800w.jpg 800w,
image-1200w.jpg 1200w"
sizes="(max-width: 600px) 100vw,
(max-width: 900px) 50vw,
800px"
alt="Responsive image"
loading="lazy">Modern Image Formats
HTML
<!-- Use WebP with fallback -->
<picture>
<source type="image/webp" srcset="image.webp">
<source type="image/jpeg" srcset="image.jpg">
<img src="image.jpg" alt="Image" loading="lazy">
</picture>
<!-- Or AVIF (even better compression) -->
<picture>
<source type="image/avif" srcset="image.avif">
<source type="image/webp" srcset="image.webp">
<img src="image.jpg" alt="Image" loading="lazy">
</picture>Image Dimensions
HTML
<!-- Always specify width and height (prevents layout shift) -->
<img src="image.jpg"
alt="Description"
width="800"
height="600"
loading="lazy">
<!-- Use aspect-ratio in CSS -->
<style>
img {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
}
</style>Optimize JavaScript Loading
defer vs async vs blocking
HTML
<!-- ⌠Blocking (blocks HTML parsing) -->
<script src="script.js"></script>
<!-- ✅ async: Download in parallel, execute ASAP -->
<script src="analytics.js" async></script>
<!-- Good for: Independent scripts (analytics, ads) -->
<!-- ✅ defer: Download in parallel, execute after HTML parsed -->
<script src="app.js" defer></script>
<!-- Good for: Scripts that need DOM, maintain order -->
<!-- ✅ Multiple deferred scripts execute in order -->
<script src="library.js" defer></script>
<script src="app.js" defer></script> <!-- Runs after library.js -->
<!-- ✅ Put scripts at end of body -->
<body>
<!-- Content -->
<script src="script.js"></script>
</body>Inline Critical JavaScript
HTML
<!-- Inline small, critical scripts -->
<script>
// Feature detection
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
// Critical functionality
const isDarkMode = localStorage.getItem('theme') === 'dark';
if (isDarkMode) {
document.documentElement.classList.add('dark');
}
</script>
<!-- Load non-critical scripts later -->
<script src="non-critical.js" defer></script>Optimize CSS Loading
Critical CSS Inline
HTML
<head>
<!-- Inline critical above-the-fold CSS -->
<style>
/* Only styles needed for initial render */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.hero {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
</style>
<!-- Defer non-critical CSS -->
<link rel="stylesheet" href="main.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="main.css"></noscript>
</head>Avoid CSS @import
HTML
<!-- ⌠CSS @import blocks rendering -->
<style>
@import url('other.css');
</style>HTML
<!-- ✅ Use link tags instead -->
<link rel="stylesheet" href="main.css">
<link rel="stylesheet" href="other.css">Resource Hints
Preconnect
HTML
<!-- Establish early connections to important origins -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">
<link rel="preconnect" href="https://api.example.com">DNS Prefetch
HTML
<!-- Resolve DNS ahead of time -->
<link rel="dns-prefetch" href="https://analytics.google.com">
<link rel="dns-prefetch" href="https://www.googletagmanager.com">Preload
HTML
<!-- Preload critical resources -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/hero-image.jpg" as="image">
<link rel="preload" href="/critical.css" as="style">
<link rel="preload" href="/app.js" as="script">Prefetch
HTML
<!-- Prefetch resources for next navigation -->
<link rel="prefetch" href="/next-page.html">
<link rel="prefetch" href="/images/next-page-hero.jpg">Minimize HTML Size
Remove Unnecessary Code
HTML
<!-- ⌠Bloated HTML -->
<div class="container">
<div class="wrapper">
<div class="inner">
<div class="content">
<p>Text</p>
</div>
</div>
</div>
</div>
<!-- Excessive comments -->
<!-- This is a div -->
<!-- This contains content -->
<div>Content</div>HTML
<!-- ✅ Minimal HTML -->
<div class="container">
<p>Text</p>
</div>
<!-- Useful comments only -->
<!-- Product Grid Section -->
<div class="product-grid">...</div>Minify HTML
HTML
<!-- Before minification (2.5KB) -->
<!DOCTYPE html>
<html>
<head>
<title>Page</title>
</head>
<body>
<div class="container">
<p>Content</p>
</div>
</body>
</html>
<!-- After minification (1.8KB) -->
<!DOCTYPE html><html><head><title>Page</title></head><body><div class="container"><p>Content</p></div></body></html>
<!-- Use build tools:
- html-minifier
- terser
- Webpack/Vite plugins -->Reduce Render-Blocking Resources
HTML
<head>
<!-- ✅ Critical resources only -->
<style>
/* Critical CSS inline */
</style>
<!-- ⌠Don't block rendering with non-critical CSS -->
<!-- <link rel="stylesheet" href="large.css"> -->
<!-- ✅ Defer non-critical CSS -->
<link rel="stylesheet" href="large.css" media="print" onload="this.media='all'">
<!-- ⌠Don't load sync scripts in head -->
<!-- <script src="app.js"></script> -->
<!-- ✅ Defer or async scripts -->
<script src="app.js" defer></script>
</head>Optimize Web Fonts
HTML
<head>
<!-- Preconnect to font provider -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Load fonts with font-display -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<!-- Or self-host fonts -->
<style>
@font-face {
font-family: 'Roboto';
src: url('/fonts/roboto.woff2') format('woff2');
font-display: swap; /* Show fallback font first */
font-weight: 400;
}
</style>
</head>Reduce Layout Shift (CLS)
Reserve Space for Images
HTML
<!-- Always specify dimensions -->
<img src="image.jpg"
alt="Description"
width="800"
height="600">
<!-- Or use CSS aspect-ratio -->
<style>
.image-container {
aspect-ratio: 16 / 9;
}
</style>Reserve Space for Ads/Embeds
HTML
<!-- Reserve space for ad -->
<div class="ad-container" style="min-height: 250px;">
<!-- Ad loads here -->
</div>
<!-- Reserve space for iframe -->
<div style="aspect-ratio: 16 / 9;">
<iframe src="video.html" style="width: 100%; height: 100%;"></iframe>
</div>Performance Measurement Tools
Google Lighthouse
- Open Chrome DevTools (F12)
- Go to Lighthouse tab
- Run audit
- Get performance score (0-100)
Core Web Vitals
- LCP (Largest Contentful Paint): < 2.5s
- FID (First Input Delay): < 100ms
- CLS (Cumulative Layout Shift): < 0.1
Other Tools
- WebPageTest: Detailed performance analysis
- PageSpeed Insights: Google's performance tool
- Chrome DevTools Performance: Record and analyze
Performance Checklist
HTML
- ☑ Minify HTML in production
- ☑ Remove unnecessary divs/wrappers
- ☑ Remove unused code
- ☑ Use semantic HTML
CSS
- ☑ Inline critical CSS
- ☑ Defer non-critical CSS
- ☑ Minify CSS
- ☑ Remove unused CSS
- ☑ Avoid @import
JavaScript
- ☑ Use defer/async
- ☑ Put scripts at end of body
- ☑ Minify JavaScript
- ☑ Code splitting
- ☑ Remove unused JavaScript
Images
- ☑ Lazy load below-the-fold images
- ☑ Use srcset for responsive images
- ☑ Use WebP/AVIF formats
- ☑ Compress images
- ☑ Specify width/height
- ☑ Use appropriate image sizes
Fonts
- ☑ Use font-display: swap
- ☑ Preload critical fonts
- ☑ Use WOFF2 format
- ☑ Limit font variations
Resources
- ☑ Preconnect to external domains
- ☑ Preload critical resources
- ☑ Use CDN for static assets
- ☑ Enable compression (gzip/brotli)
- ☑ Use caching headers