Following HTML best practices ensures your code is accessible, maintainable, performant, and future-proof. This guide covers essential practices every web developer should follow.
Document Structure
Always Include DOCTYPE
Why It Matters Tells browsers to use standards mode instead of quirks mode, ensuring consistent rendering across browsers.
HTML5 Simplicity HTML5’s DOCTYPE is simple and backwards compatible. Always use it.
Set Language and Charset
< html lang = "es" >
< head >
< meta charset = "UTF-8" />
<!-- Rest of head -->
</ head >
Benefits :
lang attribute helps screen readers pronounce content correctly
Improves SEO by indicating content language
charset="UTF-8" supports all characters worldwide
Must be declared early in the <head> section
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
Essential for responsive design . Without this, mobile devices display the desktop version zoomed out.
Semantic HTML
Use Semantic Elements
Bad (Non-Semantic)
Good (Semantic)
< div class = "header" >
< div class = "nav" >
< div class = "link" > Home </ div >
< div class = "link" > About </ div >
</ div >
</ div >
< div class = "content" >
< div class = "article" >
< div class = "title" > Article Title </ div >
< div class = "text" > Content... </ div >
</ div >
</ div >
< div class = "footer" >
< div class = "copyright" > © 2024 </ div >
</ div >
Uses generic <div> elements for everything. < header >
< nav >
< a href = "/" > Home </ a >
< a href = "/about" > About </ a >
</ nav >
</ header >
< main >
< article >
< h1 > Article Title </ h1 >
< p > Content... </ p >
</ article >
</ main >
< footer >
< small > © 2024 </ small >
</ footer >
Uses meaningful semantic elements that describe content.
Semantic elements to use :
<header>, <nav>, <main>, <article>, <section>, <aside>, <footer>
<figure>, <figcaption>, <time>, <mark>, <address>
Proper Heading Hierarchy
< h1 > Main Title </ h1 >
< h3 > Subtitle </ h3 > <!-- Skipped h2 -->
< h2 > Section </ h2 > <!-- Out of order -->
< h1 > Another Title </ h1 > <!-- Multiple h1s -->
Skips heading levels
Multiple <h1> elements
Out of logical order
< h1 > Main Page Title </ h1 >
< section >
< h2 > Section Title </ h2 >
< h3 > Subsection </ h3 >
< h3 > Another Subsection </ h3 >
</ section >
< section >
< h2 > Another Section </ h2 >
< h3 > Subsection </ h3 >
< h4 > Sub-subsection </ h4 >
</ section >
One <h1> per page
Proper hierarchy (no skipped levels)
Logical structure
SEO and Accessibility : Search engines and screen readers use heading hierarchy to understand page structure. Don’t skip levels or use multiple <h1> tags.
Accessibility
Always Include Alt Text
<!-- No alt attribute -->
< img src = "product.jpg" />
<!-- Generic alt text -->
< img src = "laptop.jpg" alt = "image" />
<!-- Filename as alt -->
< img src = "IMG_1234.jpg" alt = "IMG_1234" />
<!-- Descriptive alt text -->
< img src = "laptop.jpg" alt = "Silver gaming laptop with RGB keyboard" />
<!-- Empty alt for decorative images -->
< img src = "decorative-border.png" alt = "" />
<!-- Context-aware alt text -->
< img src = "ceo-photo.jpg" alt = "Maria Garcia, CEO of TechCorp" />
Alt text guidelines :
Describe the image’s content and function
Keep it concise (under 125 characters)
Don’t start with “image of” or “picture of”
Use empty alt (alt="") for decorative images
For complex images, consider <figcaption> or longer description
Use ARIA Labels for Icons
<!-- Icon-only button needs aria-label -->
< button type = "submit" class = "search-btn" aria-label = "Search" >
< svg > <!-- Search icon --> </ svg >
</ button >
<!-- Button with visible text doesn't need aria-label -->
< button type = "submit" class = "search-btn" >
< svg > <!-- Search icon --> </ svg >
Search
</ button >
<!-- No label -->
< input type = "email" placeholder = "Email" />
<!-- Unassociated label -->
< label > Email </ label >
< input type = "email" />
<!-- Explicit association -->
< label for = "email" > Email </ label >
< input type = "email" id = "email" name = "email" />
<!-- Implicit association -->
< label >
Email
< input type = "email" name = "email" />
</ label >
Semantic Navigation
<!-- Use nav element and lists -->
< nav aria-label = "Main navigation" >
< ul >
< li >< a href = "/" > Home </ a ></ li >
< li >< a href = "/products" > Products </ a ></ li >
< li >< a href = "/about" > About </ a ></ li >
< li >< a href = "/contact" > Contact </ a ></ li >
</ ul >
</ nav >
<!-- For multiple navs, use aria-label to distinguish -->
< nav aria-label = "Breadcrumb" >
< ol >
< li >< a href = "/" > Home </ a ></ li >
< li >< a href = "/products" > Products </ a ></ li >
< li aria-current = "page" > Laptops </ li >
</ ol >
</ nav >
Optimize Images
<!-- Use loading='lazy' for images below the fold -->
< img
src = "product.jpg"
alt = "Gaming Laptop"
loading = "lazy"
width = "800"
height = "600"
/>
<!-- Provide width and height to prevent layout shift -->
< img
src = "hero.jpg"
alt = "Hero image"
width = "1920"
height = "1080"
/>
Benefits :
loading="lazy": Defers loading until image is near viewport
width and height: Prevents Cumulative Layout Shift (CLS)
Improves Core Web Vitals
Preconnect to External Domains
< head >
<!-- Preconnect to external API -->
< link rel = "preconnect" href = "https://api.example.com" />
<!-- Preconnect to Google Fonts -->
< link rel = "preconnect" href = "https://fonts.googleapis.com" />
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin />
</ head >
Benefits : Establishes early connections, reducing latency when fetching resources.
Script Loading Strategy
Default (Bad)
Defer (Good)
Async (Use Carefully)
Module (Modern)
< head >
<!-- Blocks HTML parsing -->
< script src = "large-script.js" ></ script >
</ head >
Script downloads and executes immediately, blocking page render. < head >
<!-- Downloads in parallel, executes after HTML parsing -->
< script src = "script.js" defer ></ script >
</ head >
Script downloads in parallel but executes only after HTML is parsed. < head >
<!-- Downloads and executes ASAP -->
< script src = "analytics.js" async ></ script >
</ head >
Script downloads and executes as soon as possible, without waiting. < body >
<!-- ES modules, deferred by default -->
< script type = "module" src = "/src/main.ts" ></ script >
</ body >
ES6 modules with defer behavior by default.
When to use each :
No attribute : Critical scripts that must run before page render
defer : Most scripts (executes in order, after DOM ready)
async : Independent scripts (analytics, ads)
type=“module” : Modern ES6 modules
Code Quality
< DIV CLASS = "Container" >
< IMG SRC = "image.jpg" ALT = "Image" />
</ DIV >
< div class = "container" >
< img src = "image.jpg" alt = "Image" />
</ div >
Quote Attribute Values
< input type = text class = form-input />
< a href = /products > Products </ a >
< input type = "text" class = "form-input" />
< a href = "/products" > Products </ a >
Close Void Elements Consistently
<!-- HTML5 allows both, pick one style and stick to it -->
<!-- Self-closing (XHTML style) -->
< img src = "image.jpg" alt = "Image" />
< br />
< meta charset = "UTF-8" />
<!-- No self-closing (HTML5 style) -->
< img src = "image.jpg" alt = "Image" >
< br >
< meta charset = "UTF-8" >
Both styles are valid in HTML5. Choose one and use it consistently throughout your project.
<!-- Good indentation and structure -->
< header class = "header" >
< div class = "header__container" >
< nav class = "header__nav" >
< ul class = "header__nav-list" >
< li class = "header__nav-item" >
< a href = "/" class = "header__nav-link" > Home </ a >
</ li >
< li class = "header__nav-item" >
< a href = "/products" class = "header__nav-link" > Products </ a >
</ li >
</ ul >
</ nav >
</ div >
</ header >
Best practices :
Use consistent indentation (2 spaces or 4 spaces)
One element per line for block elements
Group related attributes
Use formatting tools (Prettier, etc.)
SEO
Unique, Descriptive Titles
< title > Home </ title >
< title > Page </ title >
< title > Welcome to our website where we sell products </ title >
Too generic, too vague, or too long. < title > Gaming Laptops - ML Store </ title >
< title > Contact Us | ML Store </ title >
< title > How to Choose a Laptop | ML Store Blog </ title >
Descriptive, includes keywords, includes brand.
Title best practices :
50-60 characters max
Include primary keyword
Add brand name
Make each page title unique
< meta
name = "description"
content = "Shop the best gaming laptops at ML Store. Free shipping on orders over $299. Expert reviews and top brands."
/>
Best practices :
150-160 characters
Include primary keyword naturally
Write compelling copy (it appears in search results)
Unique for each page
Structured Data
<!-- JSON-LD structured data for products -->
< script type = "application/ld+json" >
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Gaming Laptop Pro",
"image": "https://example.com/laptop.jpg",
"description": "High-performance gaming laptop",
"brand": "TechBrand",
"offers": {
"@type": "Offer",
"price": "1299.99",
"priceCurrency": "USD"
}
}
</ script >
Helps search engines understand your content better, potentially showing rich results.
Common Anti-Patterns to Avoid
Don't Use Tables for Layout Tables are for tabular data only. Use CSS Grid or Flexbox for layouts. <!-- Bad -->
< table >
< tr >
< td > Header </ td >
</ tr >
< tr >
< td > Content </ td >
</ tr >
</ table >
Don't Use Inline Styles Use external CSS files or CSS classes. Inline styles are hard to maintain. <!-- Bad -->
< div style = "color: red; margin: 10px;" >
Content
</ div >
Don't Use Deprecated Tags Avoid <center>, <font>, <marquee>, <blink>, <frame>, etc. <!-- Bad -->
< center >
< font color = "red" > Text </ font >
</ center >
Don't Omit Required Attributes Always include required attributes like alt, src, href, type. <!-- Bad -->
< img src = "image.jpg" />
< a > Click here </ a >
Validation
Validate Your HTML
Use the W3C Markup Validation Service to check for:
Syntax errors
Missing required attributes
Deprecated elements
Improper nesting
<!-- Example of validation catching errors -->
<!-- Invalid: Missing alt attribute -->
< img src = "logo.png" />
<!-- Invalid: Improper nesting -->
< p > Paragraph < div > Block inside inline </ div ></ p >
<!-- Invalid: Duplicate IDs -->
< div id = "content" ></ div >
< div id = "content" ></ div >
Quick Reference Checklist
Summary
Following these best practices will help you create:
Accessible websites that work for everyone
Performant pages that load quickly
Maintainable code that’s easy to update
SEO-friendly content that ranks well
Standards-compliant HTML that works across browsers