---
title: "HTML: Semantic Elements"
slug: "html-semantic-elements"
category: "frontend"
tags: ["frontend", "html", "semantics", "accessibility", "seo"]
status: "stable"
last_updated: 2026-05-14
summary: "Use the element that names the role: article, section, nav, main, aside, header, footer over div soup."
related: ["[[frontend/html]]", "[[frontend/accessibility]]", "[[frontend/forms]]", "[[frontend/html-aria-patterns]]", "[[seo/technical]]"]
---

## Overview

Every HTML element carries an implicit ARIA role that assistive technology, search crawlers, and browser reader modes consume. A page of nested `<div>` elements gives those consumers nothing. Replace each `<div>` with the element that names what the region actually is, and the document becomes navigable without a single line of JavaScript.

## `<main>` holds the page's primary content

Use exactly one `<main>` per page. It wraps the content that is unique to this URL, not the chrome repeated across pages. Screen readers expose a "main" landmark that lets users jump straight to content, skipping headers and nav. Skip links work the same way: `<a href="#main">Skip to content</a>` anchors to this element.

```html
<body>
  <header><!-- site chrome --></header>
  <nav><!-- primary nav --></nav>
  <main id="main">
    <!-- unique page content -->
  </main>
  <footer><!-- site footer --></footer>
</body>
```

## `<article>` wraps self-contained content

Use `<article>` when the block makes sense on its own: a blog post, a news story, a product card, a comment. Self-contained means the content could be syndicated or embedded elsewhere without losing meaning. Articles can nest when a comment thread lives inside a post.

```html
<article>
  <header>
    <h2>Postgres 17 Released</h2>
    <time datetime="2026-05-01">May 1, 2026</time>
  </header>
  <p>The release ships logical replication improvements...</p>
</article>
```

## `<section>` groups thematic content within a page or article

Use `<section>` for a named thematic block that belongs to a larger whole. Every `<section>` needs a heading (`<h2>` through `<h6>`) so the outline is legible. Do not use `<section>` as a generic container; that is what `<div>` is for.

A section is wrong when the content is self-contained (use `<article>`) or when it is only there for styling (use `<div>`).

## `<nav>` wraps navigation links

Use `<nav>` for the primary site navigation and for secondary nav blocks like a table of contents or pagination. Multiple `<nav>` elements are fine; give each an `aria-label` or `aria-labelledby` so users can distinguish them.

```html
<nav aria-label="Primary">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/blog">Blog</a></li>
  </ul>
</nav>
<nav aria-label="Pagination">
  <a href="/page/2">Next</a>
</nav>
```

Do not wrap every list of links in `<nav>`. Footer link lists for legal pages or social icons are not navigation landmarks.

## `<aside>` holds tangential content

Use `<aside>` for content that is related to the main flow but not essential to it: a pull quote, a sidebar, a related-links callout, a tip box. When `<aside>` is inside an `<article>`, it is tangential to the article. At the page level, it is tangential to the document.

Avoid using `<aside>` for purely decorative panels or for any content that is required to understand the main text.

## `<header>` and `<footer>` scope to their container

`<header>` and `<footer>` are not global elements. Each scopes to its nearest sectioning element (`<body>`, `<article>`, `<section>`). A page can have a document header, an article header, and an article footer all at once.

```html
<article>
  <header>
    <h2>Article Title</h2>
    <p>By Author, <time datetime="2026-05-14">May 14, 2026</time></p>
  </header>
  <p>Body...</p>
  <footer>
    <p>Tags: HTML, Semantics</p>
  </footer>
</article>
```

## Avoid div soup; check every container

Before adding a `<div>`, ask what the block is. If it is primary page content, use `<main>`. If it is a self-contained piece, use `<article>`. If it is a thematic group with a heading, use `<section>`. If it is navigation, use `<nav>`. If it is tangential, use `<aside>`. Reserve `<div>` for layout containers that carry no meaning.

The benefit is concrete: screen reader users navigate by landmarks. Search crawlers weight heading text inside `<article>` differently from boilerplate in `<header>`. See [[frontend/html]] for the heading-order rules that pair with this landmark structure, and [[frontend/accessibility]] for the WCAG conformance requirements these elements satisfy. For adding ARIA on top of semantic elements, see [[frontend/html-aria-patterns]].

## Related

- [[frontend/html]]
- [[frontend/accessibility]]
- [[frontend/forms]]
- [[frontend/html-aria-patterns]]
- [[seo/technical]]
