12 Navigation Patterns and When to Use Each, Plus a Mobile Sidebar That Works
01Navigation Is a Decision, Not a Default
Most interfaces inherit their navigation pattern from whatever the designer used last time, or whatever the framework ships with by default. That's how you end up with a sidebar on a three-page marketing site or a tab bar on a tool with forty distinct workflows.
Navigation isn't decoration. It's a structural promise to the user: here's where you are, here's where you can go, and here's how much mental effort that will cost you. Break that promise and every subsequent interaction suffers.
This post has two parts. First, a catalog of twelve navigation patterns, each with the conditions that make it the right call and the red flags that mean you should look elsewhere. Second, a closer look at one of the most common patterns, the sidebar that collapses into a hamburger menu on mobile, and what it takes to make it work for keyboard and screen reader users, not just visually.
02The Catalog
1. Persistent Sidebar
A vertical panel, usually on the left, that stays visible as the user moves through the product. Destinations are listed with icons and labels.
Use when: The product has 5–15 top-level sections, users switch between them frequently, and screen real estate is available (typically desktop). Think dashboards, admin panels, SaaS tools.
Avoid when: You're on mobile without a collapse mechanism, or when users follow a strict linear path through content. A persistent menu becomes visual noise in that context.
2. Collapsible / Icon-Only Sidebar
Same structure as above, but the sidebar collapses to show only icons. Hovering or a toggle expands it.
Use when: Your user base is power users who learn icon meanings quickly, and you need to recover horizontal space for content-heavy views.
Avoid when: Your product has new or infrequent users. Unlabeled icons are a recognition failure waiting to happen. Even familiar icons carry ambiguity without labels.
3. Horizontal Top Navigation
A bar across the top of the viewport with destination links. The classic web pattern.
Use when: You have 3–7 top-level destinations, content is document-like or marketing-oriented, and users scan left-to-right. Works well for public-facing sites.
Avoid when: You exceed seven items. The bar breaks, wraps, or forces dropdowns that obscure content. It also scales poorly as the product grows.
4. Mega Menu
A wide, panel-style dropdown triggered from a top nav item. Can contain grouped links, images, and short descriptions.
Use when: You have a large, flat information architecture with many peer-level destinations: e-commerce, news sites, large enterprise homepages.
Avoid when: Your hierarchy is shallow. A mega menu implies complexity; using it on simple sites makes navigation feel more overwhelming than it is.
5. Tab Bar (Mobile Bottom Navigation)
3–5 destinations pinned to the bottom of the screen as tappable icons with labels. The dominant pattern in native mobile apps.
Use when: You're building a mobile-first web app or PWA with 3–5 equally important top-level sections that users visit in any order. Thumb-reachable and always visible.
Avoid when: You have more than five destinations; additional tabs create cognitive overload. Also avoid on desktop-primary interfaces where the bottom strip feels out of place.
6. Horizontal Tab Strip (Content-Level)
Tabs rendered within a page or panel to switch between related views of the same subject. Different from global navigation: this is local navigation.
Use when: You need to show multiple facets of the same entity: a user profile with tabs for Overview, Activity, and Settings; a product page with Details, Reviews, and Specs.
Avoid when: The tabs represent genuinely separate destinations. Forcing unrelated content into a tabbed view creates a false sense of relationship between sections.
7. Breadcrumbs
A horizontal trail showing the user's current location within a hierarchy: Home > Products > Shoes > Running.
Use when: Your content lives in a deep, tree-structured hierarchy: e-commerce categories, documentation, file systems. Breadcrumbs answer "where am I?" without the user having to think.
Avoid when: Your site is flat or only one or two levels deep. Breadcrumbs add visual weight without payoff if every trail reads Home > Page.
8. Stepper / Progress Navigation
A linear sequence of named steps displayed horizontally or vertically, with the current step highlighted and completed steps marked.
Use when: The user is completing a multi-step process with a defined order: checkout, onboarding, form wizards. The stepper sets expectations and shows progress.
Avoid when: Steps can be completed in any order, or when the flow is short enough to fit on one screen. A three-field form with a four-step stepper is UX theater.
9. Hamburger Menu
An icon (three horizontal lines) that toggles a hidden navigation panel. Ubiquitous on mobile.
Use when: You genuinely have no room for persistent navigation, typically on mobile viewports for secondary or auxiliary links. Acceptable for infrequently used destinations.
Avoid when: The hidden menu contains your most critical navigation. Hiding primary actions behind a hamburger reduces discoverability and increases time-to-navigate. If users need it constantly, it shouldn't be hidden.
10. Command Palette
A keyboard-triggered modal (usually Cmd+K or Ctrl+K) with a fuzzy search input that lets users jump to any destination or trigger any action by typing.
Use when: Your tool has a large number of actions and destinations, and your users are power users who prefer keyboards. Popularized by VS Code, Linear, Raycast, and Notion. It dramatically speeds up expert workflows.
Avoid when: Your users are non-technical or infrequent visitors. A command palette is invisible until you know it exists. It supplements navigation; it rarely replaces it. Always pair it with visible navigation for new users.
11. Contextual / Floating Action Button (FAB)
A prominent circular button, usually bottom-right, that triggers the primary action on a screen. Sometimes expands into a small menu of related actions.
Use when: There is a single, dominant action on a screen that users will perform repeatedly: compose, add, create. Common in mobile-first interfaces.
Avoid when: You have multiple equally important actions. Turning the FAB into a menu of five options defeats the purpose of having a single prominent affordance.
12. Pagination vs. Infinite Scroll vs. Load More
Technically these are content navigation patterns rather than structural navigation, but they govern how users move through large data sets, and they're consistently misused.
- Pagination: Use when users need to return to a specific position (search results, admin tables, filtered lists). A user who found something on page 4 can get back to page 4.
- Infinite scroll: Use when content is consumable and positional memory doesn't matter: social feeds, image galleries. Never use on e-commerce or search results where users compare and backtrack.
- Load More button: The compromise. Preserves scroll position, gives users control, works well for feeds where users want some but not infinite content.
03The Meta-Pattern: Combine With Intention
Most mature products use multiple navigation patterns at once, and that's fine. A sidebar for global navigation, tab strips for local context switching, breadcrumbs for deep hierarchy, and a command palette for power users is a coherent, layered system. The failure mode is layering by accident: a sidebar and a top nav and a hamburger menu, all pointing to different subsets of the same destinations.
Every navigation pattern you add is a new question users have to answer: which of these should I use right now? Make sure the answer is always obvious.
04A Quick Decision Framework
- How many top-level destinations? Under 5 → top nav. 5–15 → sidebar. Over 15 → reconsider your IA before choosing a pattern.
- How often do users switch destinations? Constantly → persistent navigation. Rarely → hamburger or secondary nav is fine.
- Who are the users? Power users → invest in a command palette. General public → make everything visible and labeled.
- What device? Mobile → tab bar or hamburger. Desktop → sidebar or top nav. Both → responsive collapsing sidebar or separate mobile bottom nav.
- Is the flow linear? Yes → stepper. No → standard navigation.
05Making the Mobile Sidebar Actually Work
The collapsing sidebar is one of the patterns that looks solved. Hamburger menus have existed forever and every CSS framework ships one. But "widely used" and "correctly implemented" are not the same thing, and on mobile the gap between them can trap real users.
The core mistake is building the sidebar around visual state instead of interaction state. Test it visually (does it slide in? does it close when I tap the overlay?) and it passes. Test how a keyboard or screen reader user actually moves through it and it can fail in three separate ways.
The typical starting point
Picture a sidebar built in vanilla JS with a CSS transition. A toggle button sets a data-open attribute on the sidebar, CSS handles the transform from translateX(-100%) to translateX(0), and a semi-transparent overlay behind it can be tapped to close it:
const sidebar = document.querySelector('.sidebar');
const overlay = document.querySelector('.sidebar-overlay');
const toggleBtn = document.querySelector('.sidebar-toggle');
toggleBtn.addEventListener('click', () => {
const isOpen = sidebar.dataset.open === 'true';
sidebar.dataset.open = isOpen ? 'false' : 'true';
overlay.classList.toggle('visible', !isOpen);
document.body.classList.toggle('sidebar-open', !isOpen);
});
overlay.addEventListener('click', () => {
sidebar.dataset.open = 'false';
overlay.classList.remove('visible');
document.body.classList.remove('sidebar-open');
});
And .sidebar-open on the body is supposed to prevent background scrolling:
.sidebar-open {
overflow: hidden;
}
On paper this looks fine. In practice it has three failure modes.
Failure mode 1: background scroll on iOS Safari
overflow: hidden on body doesn't fully work on iOS Safari. Safari can let the page keep scrolling beneath a fixed overlay. Users open the sidebar, swipe on the overlay, and the content behind it scrolls. It's disorienting, and it makes the overlay feel broken because the tap target for dismissal seems to move.
The fix needs two additions: store the scroll position before locking and restore it afterward, and set the body to position: fixed while the sidebar is open, which forces iOS Safari to respect the lock:
let scrollY = 0; function lockScroll() { scrollY = window.scrollY; document.body.style.top = `-${scrollY}px`; document.body.classList.add('sidebar-open'); } function unlockScroll() { document.body.classList.remove('sidebar-open'); document.body.style.top = ''; window.scrollTo(0, scrollY); }
.sidebar-open {
position: fixed;
overflow-y: scroll;
width: 100%;
}
overflow-y: scroll keeps the scrollbar gutter visible so the layout doesn't jump. The top offset preserves the scroll position across the lock/unlock cycle. It's worth keeping as a small snippet to drop into any sidebar implementation from the start.
Failure mode 2: unmanaged focus
This is the one that makes the menu feel like a trap for screen reader users.
When the sidebar opens, focus stays wherever it was on the page, often behind the overlay. Screen reader users try to navigate and find themselves reading content that's visually hidden, or cycling through interactive elements in the background instead of inside the sidebar.
When the sidebar closes, focus just drops. It doesn't return to the toggle button. In VoiceOver, that means the reading cursor jumps somewhere unpredictable, sometimes back to the top of the page.
The fix is explicit focus management on both open and close:
function openSidebar() { sidebar.dataset.open = 'true'; overlay.classList.add('visible'); lockScroll(); // Move focus into the sidebar const firstFocusable = sidebar.querySelector( 'a, button, [tabindex]:not([tabindex="-1"])' ); if (firstFocusable) firstFocusable.focus(); } function closeSidebar() { sidebar.dataset.open = 'false'; overlay.classList.remove('visible'); unlockScroll(); // Restore focus to the toggle button toggleBtn.focus(); }
There's still a subtler problem: while the sidebar is open, the Tab key can carry focus back out into the background content. A focus trap keeps it inside:
function trapFocus(element) { const focusable = element.querySelectorAll( 'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])' ); const first = focusable[0]; const last = focusable[focusable.length - 1]; element.addEventListener('keydown', function handler(e) { if (e.key !== 'Tab') return; if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault(); last.focus(); } } else { if (document.activeElement === last) { e.preventDefault(); first.focus(); } } // Clean up when sidebar closes if (!sidebar.dataset.open || sidebar.dataset.open === 'false') { element.removeEventListener('keydown', handler); } }); }
Call trapFocus(sidebar) right after openSidebar(). This is a minimal implementation. Libraries like focus-trap handle more edge cases, but for a simple sidebar nav this covers the basics without adding a dependency.
Failure mode 3: missing ARIA on the toggle button
Without aria-expanded on the toggle button and aria-controls pointing at the sidebar, screen readers can't tell the user whether the sidebar is open or closed. That state is purely visual.
The markup is a one-time fix, but the attributes have to stay in sync with the open/close state at runtime:
<button class="sidebar-toggle" aria-expanded="false" aria-controls="main-sidebar" aria-label="Open navigation menu" > <span class="hamburger-icon"></span> </button> <nav id="main-sidebar" class="sidebar" aria-label="Main navigation"> <!-- nav links --> </nav>
// In JS, update aria-expanded on state change function openSidebar() { toggleBtn.setAttribute('aria-expanded', 'true'); toggleBtn.setAttribute('aria-label', 'Close navigation menu'); // ... rest of open logic } function closeSidebar() { toggleBtn.setAttribute('aria-expanded', 'false'); toggleBtn.setAttribute('aria-label', 'Open navigation menu'); // ... rest of close logic }
Small additions, but they're the difference between a screen reader announcing "Open navigation menu, collapsed, button" and just "button". That context matters to the people who rely on it.
Don't forget Escape
Users expect Escape to close the sidebar from anywhere inside it:
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && sidebar.dataset.open === 'true') { closeSidebar(); } });
The full sequence
Put together, the sidebar does the following, in order:
- On open: lock scroll (with the iOS-compatible fixed-position approach), update
data-open, updatearia-expandedandaria-label, move focus to the first focusable element inside the sidebar, activate the focus trap. - On close (from any source: button, overlay tap, Escape key): unlock scroll and restore the position, update
data-open, updatearia-expandedandaria-label, return focus to the toggle button.
A few habits that prevent all three failures:
- Test with VoiceOver on iOS and TalkBack on Android before any mobile nav ships. Not a full accessibility audit, just a real walkthrough of opening and closing the nav with the screen reader on.
- Write open and close as explicit paired functions, not a single toggle. The paired structure forces you to think about what has to happen in both directions, including focus and scroll state.
- Treat iOS Safari scroll lock as a special case that always needs the fixed-position approach.
- ARIA on the toggle button is not optional.
aria-expanded,aria-controlsand a meaningfularia-labelthat updates on state change are baseline requirements, not enhancements.
Focus and screen reader problems are also the slowest to surface. They only get reported by users who rely on assistive tech, and those users often just leave rather than file a bug report. Navigation sits where visual design, interaction design and accessibility meet, and it's easy to get two of the three right and miss the third. Interaction state and visual state can quietly drift apart in other components too; the post on search filter UX and UI state covers some adjacent cases.
06Final Thought
Navigation patterns aren't about aesthetics. They're about managing cognitive load. The best navigation is the one users don't notice because it always puts them exactly where they expected to be. Run through this list the next time you're starting a project or auditing an existing one, and ask not "what looks good" but "what makes the right things obvious", for every user, including the ones navigating by keyboard or screen reader.
07Frequently Asked Questions
Why does focus management matter so much for sidebar navigation on mobile?
On mobile, keyboard and screen reader users, including those using switch access or external keyboards, can get trapped inside an open sidebar if focus isn't explicitly controlled. Without moving focus in on open and restoring it on close, users lose their place in the page entirely, which is a real accessibility failure, not just a cosmetic one.
What's the simplest way to prevent background scrolling when a mobile sidebar is open?
Setting overflow: hidden on the body is the usual baseline, but it isn't enough on iOS Safari. There you also need to set the body to position: fixed while the sidebar is open, store the scroll position before locking, and restore it accurately when the sidebar closes.
Should a sidebar overlay be a <nav>, a <dialog>, or something else semantically?
For a full-screen or large overlay sidebar, <dialog> with role="dialog" and aria-modal="true" is often the right call because browsers and screen readers understand its open/close semantics natively. For a persistent or partially visible sidebar, <nav> with aria-expanded and aria-controls on the toggle button is more appropriate.
Denny Pratama
Product designer in Indonesia. Since 2018 I’ve designed SaaS products, dashboards and marketing sites, and now I build my own designs too.