Why Table of Contents Matter
A table of contents (TOC) is one of the most effective navigation tools for long-form content on the web. When implemented correctly, it provides users with a scannable overview of page content, enables direct access to specific sections, and significantly improves the overall user experience.
Research from Nielsen Norman Group shows that users dedicate significantly more attention to the top of web pages, meaning content at the bottom often suffers from low engagement. A well-designed table of contents addresses this by increasing the discoverability of content throughout the page and helping users understand what the page offers before diving into details.
Key Benefits
- Scannable overview: Users can quickly understand page structure and content scope without reading everything
- Direct access: Clickable headings allow jumping to specific sections without tedious scrolling
- Shareable sections: Each TOC entry links to a unique URL fragment, enabling precise sharing of specific content areas
- SEO benefits: Search engines can detect and display section headings as sitelinks, improving search result presentation
For modern frameworks like Next.js, a table of contents serves multiple purposes beyond navigation. It contributes to accessibility by providing keyboard-accessible shortcuts, enhances SEO through structured content hierarchy, and improves Core Web Vitals metrics like Largest Contentful Paint by reducing unnecessary content loading.
This guide covers everything you need to create a perfect table of contents using HTML, CSS, and JavaScript--from semantic markup to smooth scroll animations and active state highlighting.
HTML Structure: Building a Semantic Foundation
The foundation of any great table of contents starts with proper HTML semantics. Using the correct elements ensures screen readers and other assistive technologies can interpret the TOC correctly. This approach aligns with our commitment to accessible web development practices that serve all users.
Semantic Markup with nav Element
The <nav> element creates a dedicated region for navigation content. Pair it with aria-label to clearly identify the purpose of this navigation region for screen reader users.
<nav class="table-of-contents" aria-label="Table of contents">
<h2>Table of Contents</h2>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#html-structure">HTML Structure</a></li>
<li><a href="#css-styling">CSS Styling</a></li>
<li><a href="#javascript">JavaScript</a></li>
<li><a href="#ux-considerations">UX Considerations</a></li>
</ul>
</nav>
Key semantic considerations include using the <nav> element with aria-label to identify the navigation region, maintaining proper heading hierarchy with an H2 for the TOC title, using an unordered list <ul> with list items <li> for navigation entries, and ensuring anchor links point to section IDs on the page.
Content Section IDs
Each section targeted by the TOC needs a unique ID attribute. These IDs create the URL fragments that enable both navigation and section sharing.
<section id="introduction">
<h2>Introduction</h2>
<p>Content goes here...</p>
</section>
<section id="html-structure">
<h2>HTML Structure</h2>
<p>Content goes here...</p>
</section>
Nested Table of Contents
For deeply structured content, nested TOCs provide hierarchical navigation. This is common in documentation sites and lengthy guides where subsections need their own entries.
<nav class="table-of-contents" aria-label="Table of contents">
<h2>Table of Contents</h2>
<ul>
<li>
<a href="#getting-started">Getting Started</a>
<ul>
<li><a href="#installation">Installation</a></li>
<li><a href="#configuration">Configuration</a></li>
</ul>
</li>
<li>
<a href="#advanced-topics">Advanced Topics</a>
<ul>
<li><a href="#performance">Performance Optimization</a></li>
<li><a href="#accessibility">Accessibility</a></li>
</ul>
</li>
</ul>
</nav>
When creating nested structures, maintain consistent indentation and consider using CSS to visually distinguish hierarchy levels. Nested lists should be properly structured with parent <li> elements wrapping the nested <ul> for correct semantics.
1<nav class="table-of-contents" aria-label="Table of contents">2 <h2>Table of Contents</h2>3 <ul>4 <li><a href="#introduction">Introduction</a></li>5 <li><a href="#html-structure">HTML Structure</a></li>6 <li><a href="#css-styling">CSS Styling</a></li>7 <li><a href="#javascript">JavaScript</a></li>8 </ul>9</nav>CSS Styling: Layout and Positioning
One of the most popular TOC patterns is the sticky sidebar, which keeps the navigation visible as users scroll through long content. CSS makes this straightforward with position: sticky. This pattern is a staple of modern web application development that prioritizes user experience.
Sticky Positioning
The sticky positioning model allows an element to remain in the normal document flow until it reaches a specified scroll position, at which point it becomes fixed relative to its containing block.
.table-of-contents {
position: sticky;
top: 2rem;
max-height: calc(100vh - 4rem);
overflow-y: auto;
padding: 1rem;
}
According to Nielsen Norman Group research, sticky TOCs work best when placed in a left or right rail rather than the main content body. Main-body sticky navigation can compete with site-wide navigation elements, creating a confusing vertical arrangement that mimics local navigation patterns.
Layout Options
Vertical Layout remains the most common and effective pattern for tables of contents with many items. It provides clear visual hierarchy and works well with both single and nested list structures.
.table-of-contents ul {
list-style: none;
padding: 0;
margin: 0;
}
.table-of-contents li {
margin-bottom: 0.5rem;
}
.table-of-contents a {
display: block;
padding: 0.5rem 1rem;
text-decoration: none;
border-left: 3px solid transparent;
transition: all 0.2s ease;
}
Horizontal Layout saves vertical space but may be harder to scan. Best for TOCs with fewer items and shorter headings.
Multi-Column Layout offers efficient use of space for TOCs with many items while maintaining scannability.
Visual States and Active Highlighting
The CSS-Tricks implementation demonstrates effective visual feedback for active states. Key styling patterns include hover effects, active state indicators, and smooth transitions.
/* Default state */
.table-of-contents a {
color: inherit;
text-decoration: none;
transition: all 0.2s ease;
}
/* Hover state */
.table-of-contents a:hover {
color: #0066cc;
}
/* Active/current section state */
.table-of-contents a.active {
font-weight: 600;
color: #0066cc;
border-left-color: #0066cc;
background-color: rgba(0, 102, 204, 0.1);
}
/* Nested hierarchy indicators */
.table-of-contents ul ul a {
padding-left: 2rem;
font-size: 0.9em;
}
1.table-of-contents {2 position: sticky;3 top: 2rem;4 max-height: calc(100vh - 4rem);5 overflow-y: auto;6 padding: 1rem;7}8 9.table-of-contents a.active {10 font-weight: 600;11 color: #0066cc;12 border-left-color: #0066cc;13 background-color: rgba(0, 102, 204, 0.1);14}JavaScript: Scroll Spy and Active State Management
The modern approach to scroll spying uses the Intersection Observer API, which is significantly more performant than scroll event listeners that fire continuously during scrolling. This API is a cornerstone of interactive JavaScript development for performance-conscious applications.
IntersectionObserver Implementation
The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or the viewport. This makes it ideal for scroll spy functionality without impacting scroll performance.
document.addEventListener('DOMContentLoaded', () => {
const tocLinks = document.querySelectorAll('.table-of-contents a');
const sections = Array.from(tocLinks).map(link => {
const id = link.getAttribute('href').slice(1);
return document.getElementById(id);
}).filter(Boolean);
if (sections.length === 0) return;
const observerOptions = {
rootMargin: '-10% 0px -80% 0px',
threshold: 0
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const id = entry.target.getAttribute('id');
updateActiveLink(id);
}
});
}, observerOptions);
sections.forEach(section => observer.observe(section));
});
function updateActiveLink(activeId) {
const tocLinks = document.querySelectorAll('.table-of-contents a');
tocLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${activeId}`) {
link.classList.add('active');
}
});
}
Smooth Scrolling
The simplest approach to smooth scrolling uses CSS alone, supported by all modern browsers through the scroll-behavior property.
html {
scroll-behavior: smooth;
}
For browsers that don't support scroll-behavior: smooth, or for more control over the scroll animation, JavaScript can provide the fallback functionality.
function scrollToSection(targetId) {
const targetElement = document.getElementById(targetId);
if (!targetElement) return;
targetElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
Handling Edge Cases
Multiple sections may be visible at once on larger screens. A robust implementation considers which section should be considered "active" when sections overlap, ensuring the most relevant section is highlighted.
The key is to use rootMargin strategically to define which portion of the viewport triggers the active state. A common pattern uses negative margins at the top and bottom to trigger when a section is near the top of the viewport.
1const observerOptions = {2 rootMargin: '-10% 0px -80% 0px',3 threshold: 04};5 6const observer = new IntersectionObserver((entries) => {7 entries.forEach(entry => {8 if (entry.isIntersecting) {9 const id = entry.target.getAttribute('id');10 updateActiveLink(id);11 }12 });13}, observerOptions);14 15sections.forEach(section => observer.observe(section));UX Best Practices: Placement and Design
Research from Nielsen Norman Group provides clear guidance on TOC design. Placement significantly impacts usability--whether in the main body, left rail, or right rail. These principles are essential for creating user-centric digital experiences that drive engagement.
Placement Considerations
Main Body Placement works well when side rails are occupied by other navigation. It's adaptable across all screen sizes but should be placed at the top of content or just below a page summary to ensure visibility.
Left Rail Placement is a very common pattern for documentation sites. It works with multi-column layouts on large screens and provides clear visual separation from main content.
Right Rail Considerations require more attention. This placement is less common due to "right-rail blindness" where users often ignore content on the right side of the screen. However, it can work if designed simply without graphical elements that resemble advertisements. Clear labeling like "On This Page" helps avoid confusion.
Sticky vs Non-Sticky Decisions
| Location | Recommendation |
|---|---|
| Left or Right Rail | Make it sticky |
| Main Body Content | Keep it non-sticky to avoid confusion with global navigation |
A common pitfall observed by NN/g is when sticky main-body TOCs stack with global navigation, creating a confusing vertical arrangement. When placing a TOC in a sidebar, sticky positioning provides the best user experience for long content.
Styling Best Practices
Research-backed styling recommendations include using different font sizes or weights for main sections versus subsections to create clear visual hierarchy, using subtle borders or backgrounds to separate TOC from content, maintaining consistent link styling that indicates in-page links versus external links, and avoiding ad-like appearance since simple, non-graphical designs perform better than heavily styled boxes.
Labeling and Signifiers
Always include a clear header for the TOC to prevent confusion. Common and effective labels include "Table of Contents," "On This Page," and "In This Article." Icons can help users understand that links navigate to in-page sections rather than new pages, improving the overall user experience.
Mobile Responsiveness and Performance
Mobile design requires different TOC patterns due to the lack of side rails and limited screen space. A desktop-first approach won't work--mobile users need thoughtful alternatives. Mobile-first development is a core principle of our responsive web design services.
Mobile Patterns
Move to Main Body is the most straightforward approach. Place the TOC at the top of the content, but be cautious of the fold--too much content before the TOC can push it out of initial view on smaller screens.
Accordion-Style TOC collapses the TOC behind a button or accordion to save space. This pattern works well when the TOC contains many items.
@media (max-width: 768px) {
.table-of-contents {
position: relative;
top: 0;
}
.table-of-contents .toc-content {
display: none;
}
.table-of-contents.open .toc-content {
display: block;
}
}
Sticky Collapsed TOC offers a small, sticky button that expands to show the full TOC when tapped. This minimizes space while keeping navigation accessible.
Performance Optimization
Reducing Layout Shift is critical for both user experience and Core Web Vitals. When implementing sticky TOCs, prevent layout shift by reserving space for the TOC in the document flow, using CSS containment for improved rendering performance, and avoiding sudden visibility changes that cause reflows.
Minimizing JavaScript Impact ensures the TOC doesn't negatively affect page load or scroll performance.
// Use passive event listeners for better scroll performance
document.addEventListener('scroll', handleScroll, { passive: true });
// Debounce scroll event handlers
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
Lazy Loading Considerations apply to extremely long pages. Consider dynamically generating the TOC from actual headings, lazy-loading TOC content if it's not needed immediately, and using Intersection Observer to only highlight visible sections to reduce computational overhead.
Accessibility Requirements
A fully accessible table of contents meets WCAG guidelines and works reliably with assistive technologies. These requirements ensure all users can navigate content effectively. Accessibility is not optional--it's fundamental to inclusive web development practices that serve every user.
WCAG Compliance Checklist
- Semantic structure: Use proper
<nav>,<ul>,<li>elements and maintain correct heading hierarchy throughout the page - Keyboard navigation: Ensure all interactive elements are reachable via Tab and can be activated with Enter or Space
- Focus indicators: Provide visible focus states for keyboard users so they can identify their current position
- ARIA attributes: Use
aria-labelfor navigation regions andaria-currentfor the active state - Skip links: Consider adding a "Skip to Table of Contents" link for screen reader users who want to bypass main content
Implementation Examples
<nav class="table-of-contents" aria-label="Table of contents">
<h2>On This Page</h2>
<ul>
<li><a href="#introduction" aria-current="false">Introduction</a></li>
<li><a href="#html-structure" aria-current="false">HTML Structure</a></li>
<li><a href="#css-styling" aria-current="false">CSS Styling</a></li>
</ul>
</nav>
Screen Reader Considerations
Include the TOC in the page outline but not in the main navigation region if it serves page-internal purposes. Use descriptive link text that makes sense out of context--avoid generic phrases like "click here." Consider visually hiding the TOC title while keeping it accessible to screen readers if the design requires it, using utility classes designed for this purpose.
The aria-current attribute should be updated dynamically when the scroll spy changes the active section, signaling to screen reader users which link corresponds to their current position in the content.
Complete Implementation Example
Putting it all together, this complete example demonstrates a production-ready table of contents with all the best practices covered in this guide. It combines semantic HTML, CSS sticky positioning, smooth scrolling, and JavaScript scroll spy using IntersectionObserver.
This implementation includes responsive design that adapts to mobile screens, proper ARIA attributes for accessibility, visual feedback for active states, and optimized performance through efficient JavaScript.
The complete code example below brings together every concept discussed throughout this guide into a cohesive, production-ready solution you can adapt for your own projects. Whether you're building a documentation site, a long-form blog post, or a comprehensive resource page, these patterns will help you create navigation that enhances rather than hinders the user experience.
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>Table of Contents Example</title>7 <style>8 html { scroll-behavior: smooth; }9 .layout { display: grid; grid-template-columns: 250px 1fr; gap: 2rem; }10 .table-of-contents { position: sticky; top: 2rem; }11 </style>12</head>13<body>14 <div class="layout">15 <nav class="table-of-contents" aria-label="Table of contents">16 <!-- TOC content -->17 </nav>18 <main class="content">19 <section id="introduction"><h2>Introduction</h2></section>20 <section id="html-semantics"><h2>HTML Semantics</h2></section>21 <!-- More sections -->22 </main>23 </div>24</body>25</html>Conclusion
Creating a perfect table of contents requires balancing multiple concerns: semantic HTML for accessibility, CSS for layout and visual feedback, JavaScript for scroll tracking, and UX research for placement and interaction design. By following the patterns and best practices outlined in this guide--drawing from established UX research and modern implementation techniques--you can build TOCs that enhance user experience, improve content discoverability, and contribute to better accessibility across all your web projects.
Remember to test your implementation across different screen sizes, ensure keyboard navigation works correctly, and consider the user's journey through your content when deciding on placement and behavior. A well-crafted table of contents becomes an essential navigation tool that users rely on to find their way through complex content.
For teams building modern web applications, implementing effective navigation patterns like these tables of contents demonstrates attention to user experience details that set professional websites apart. Consider how your table of contents connects with other aspects of your web development approach, from technical SEO to accessibility compliance and performance optimization.
Common Questions About Table of Contents
What is the best placement for a table of contents?
The best placement depends on your layout. For documentation sites, a left rail works well. For blog posts, placing the TOC at the top of the content body is common. Avoid right-rail placement as users often ignore content in that area.
How do I make my table of contents sticky?
Use CSS `position: sticky` combined with a `top` value. For example: `.table-of-contents { position: sticky; top: 2rem; }`. Make sure the parent container has sufficient height for sticky to work properly.
What is scroll spy and how do I implement it?
Scroll spy automatically highlights the TOC entry corresponding to the currently visible section. Use the Intersection Observer API for performance: create an observer that triggers when sections enter the viewport and update active states accordingly.
How do I make an accessible table of contents?
Use semantic `<nav>` with `aria-label`, maintain proper heading hierarchy, ensure keyboard navigation works, provide visible focus states, use `aria-current` for the active state, and consider screen reader-only text for titles.
Should I use CSS or JavaScript for smooth scrolling?
Start with CSS `scroll-behavior: smooth` as it's the simplest and most performant solution. Use JavaScript fallback only when you need more control or support older browsers that don't implement the CSS property.
Sources
- CSS-Tricks: Sticky Table of Contents with Scrolling Active States - Technical implementation patterns for sticky TOC with IntersectionObserver
- Nielsen Norman Group: Table of Contents - The Ultimate Design Guide - UX research findings on TOC placement, design considerations, and user experience best practices
- W3Schools: How To Create a Smooth Scrolling Effect - CSS smooth scroll syntax and implementation