Skip to content

HTML Best Practices

Follow these guidelines to write HTML that’s clean, maintainable, and works everywhere.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page Title</title>
</head>
<body>
<!-- Content -->
</body>
</html>

Always include lang on the <html> element:

<html lang="en"> <!-- English -->
<html lang="es"> <!-- Spanish -->
<html lang="zh-CN"> <!-- Chinese (Simplified) -->
<header>Site header</header>
<nav>Navigation</nav>
<main>
<article>
<h1>Article Title</h1>
<p>Content...</p>
</article>
<aside>Related links</aside>
</main>
<footer>Site footer</footer>
ElementUse For
<header>Page or section header
<nav>Navigation links
<main>Main content (one per page)
<article>Self-contained content
<section>Thematic grouping
<aside>Tangentially related content
<footer>Page or section footer

Use 2 or 4 spaces (never tabs) and be consistent:

<ul>
<li>
<a href="/page">
Link text
</a>
</li>
</ul>

Keep lines under 80-100 characters. Break long attributes:

<img
src="image.jpg"
alt="A descriptive alt text that explains the image"
width="800"
height="600"
loading="lazy"
>
<!-- Good -->
<input type="text" class="form-input">
<!-- Avoid -->
<input type=text class=form-input>

Don’t add values to boolean attributes:

<!-- Good -->
<input type="checkbox" checked disabled>
<!-- Unnecessary -->
<input type="checkbox" checked="checked" disabled="disabled">
  1. id
  2. class
  3. name
  4. data-*
  5. src, href, for, type
  6. title, alt
  7. role, aria-*
<!-- Links go somewhere -->
<a href="/about">About Us</a>
<!-- Buttons do something -->
<button type="button" onclick="save()">
Save
</button>
<a href="https://example.com"
target="_blank"
rel="noopener noreferrer">
External Site
</a>
<!-- Informative image -->
<img src="chart.png" alt="Sales grew 50% in 2024">
<!-- Decorative image -->
<img src="decorative.png" alt="">
<!-- Image is a link -->
<a href="/products">
<img src="products.png" alt="View our products">
</a>
<img
src="photo.jpg"
alt="Description"
width="800"
height="600"
>

This prevents layout shift while images load.

<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
<fieldset>
<legend>Shipping Address</legend>
<label for="street">Street</label>
<input type="text" id="street" name="street">
<label for="city">City</label>
<input type="text" id="city" name="city">
</fieldset>
<input type="email"> <!-- Email validation -->
<input type="tel"> <!-- Phone keyboard on mobile -->
<input type="url"> <!-- URL validation -->
<input type="number"> <!-- Number keyboard -->
<input type="date"> <!-- Date picker -->
<table>
<caption>Monthly Sales</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Revenue</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$10,000</td>
</tr>
</tbody>
</table>
<!-- Main navigation -->
<nav>...</nav>
<!-- User profile section -->
<section>...</section>
<!-- TODO: Add search functionality -->

Don’t over-comment obvious code.

Use the W3C Validator to check for errors:

  • Unclosed tags
  • Invalid nesting
  • Missing required attributes
  • Deprecated elements
  • DOCTYPE declared
  • lang attribute on <html>
  • <meta charset="UTF-8"> in head
  • Viewport meta tag included
  • Semantic elements used appropriately
  • All images have alt attributes
  • Form inputs have labels
  • Links use <a>, actions use <button>
  • External links have rel="noopener noreferrer"
  • HTML validates without errors
  • Consistent indentation throughout