Anchor Links: Stable In-Page Navigation Rules
Use anchor links to create stable in-page navigation, preserve heading IDs, protect cited fragment URLs, and help readers reach answers without friction.
Anchor links are in-page links that send a reader to a named section of the current document. They turn a long page into a set of durable, addressable answers—but only when every destination has a stable heading ID.
Jump to: Why anchor links matter · When to use them · Stable-ID anatomy · Implementation syntax · QA checklist
The line above is the element rendered in its compact inline form. Each link contains a fragment identifier, the part after #, and each fragment resolves to one heading on this page.
Why this element matters
Long documents create a retrieval problem before they create a reading problem. A visitor arriving from search, a support ticket, or a colleague’s message often wants one constraint rather than the whole argument. A descriptive jump link removes scrolling and reassures the visitor that the answer exists. It also supports non-linear reading: someone can check parameters, return to anatomy, and then move to QA without pretending that every useful visit starts at the introduction.
The psychological benefit depends on trust. The link text makes a promise about the destination, and the click must fulfill it immediately. A vague label such as “Learn more” forces the reader to remember surrounding context. A link that lands under a sticky header feels broken even when the browser technically reached the right coordinate. A list with 25 nearly identical entries creates another scanning task instead of reducing one.
Anchor links also improve machine extractability: the ability of software to isolate a section and preserve its meaning outside the complete page. A URL such as /guide/#renewal-terms identifies a smaller answer region than /guide/. Search systems, browser extensions, documentation tools, and AI retrieval systems can cite or share that region directly. The fragment does not create authority or guarantee a citation, but it gives an already useful section a precise public address.
That address is an interface, not an incidental by-product of the heading. Many publishing systems generate renewal-terms from the visible heading “Renewal terms.” If an editor later changes the heading to “How renewals work,” the generator may produce how-renewals-work. Every inbound link that still ends in #renewal-terms now points to an ID that no longer exists. This includes links in other articles, browser bookmarks, campaign messages, help-center replies, search results, and cited AI answers. The page URL still returns successfully, which can hide the regression, but the visitor loses the promised section-level destination.
Follow the element writing rules before adding navigation. The precedence rule still applies: anchor links navigate to content elements; they do not replace a required definition, comparison, warning, FAQ, or other typed block. The heading system owns document hierarchy and the stable IDs attached to headings. Anchor links consume those IDs.
When to use it
Use anchor links when a page contains multiple destinations that readers may need independently. Strong cases include a long reference with discrete parameters, documentation with setup and troubleshooting sections, a report with separate methodology and findings, a policy with named obligations, and an FAQ hub with question groups.
Use a visible jump-link set when at least one of these conditions is true:
- The page has five or more meaningful H2 sections and readers are likely to enter with different goals.
- The page is 1,800 words or longer and its sections can be understood non-linearly.
- Support, sales, legal, or editorial teams regularly share links to individual sections.
- A section is likely to be cited, bookmarked, revisited, or updated independently.
- Mobile readers would otherwise need substantial scrolling to reach a predictable answer.
Every substantive heading may still receive an ID even when no visible jump-link list is present. Stable destinations are cheap to preserve and expensive to reconstruct after links circulate.
Near misses deserve restraint. A three-step linear procedure should usually keep the reader moving in order; a jump list can invite someone to skip prerequisites. A short page whose headings are already visible above the fold gains no useful navigation. Tabs and accordion controls switch interface state rather than navigate to document locations, so they are not substitutes for anchor links. Pagination links move between documents. A link to another page is an internal link even if its destination happens to contain a fragment.
Do not add anchors as a keyword tactic or manufacture extra headings to obtain more fragment URLs. The unit of navigation must be a real section with a distinct purpose and enough content to satisfy the link label.
Where to place it
Place a page-level jump-link group after the hero, direct answer, or brief scope statement and before the first major body section. The reader needs to understand the page before choosing a route through it. When the page also uses a quick overview and table of contents , do not add a second list of the same destinations. The table of contents is one presentation of anchor links; configure or simplify that element instead of duplicating navigation.
Place a local jump-link group immediately before the bounded region it controls, such as an alphabetical directory or a multi-part API reference. Give it a label that names the scope: “Jump to product family” is clearer than another generic “On this page.” Place an inline cross-reference in the sentence where the destination becomes useful, using words that name the target.
Do not place anchor navigation:
- Between a heading and the paragraph that answers it.
- Between a claim and its evidence, qualification, or source.
- Beside an unrelated CTA, ad, newsletter form, or promotional card that competes for the same click.
- Inside another link, button, heading, or interactive control.
- In a sticky region that covers the target heading after navigation.
- After the content it is intended to help readers discover.
If a sticky site header is 72 pixels tall, the target needs at least that much scroll offset plus comfortable spacing. Solve this on the destination with a consistent scroll-margin-top, not by inserting empty spacer elements or changing the fragment with JavaScript.
Anatomy
An anchor-link system has seven parts:
- Navigation label: names the set, usually “On this page” or a scope-specific alternative.
- Link text: describes the destination when read without the surrounding sentence.
- Fragment href: starts with
#for the current page, such as#renewal-terms. - Target ID: the unique value on the destination, exactly matching the href without
#. - Destination heading: tells the arriving reader where they landed and what follows.
- Arrival offset: keeps the heading visible below sticky interface chrome.
- Interaction state: makes hover, keyboard focus, visited state where appropriate, and current location perceptible.
The visible heading and ID are related but not identical. The heading may change for clarity. Once public, the ID stays fixed while the section retains the same purpose. IDs use lowercase ASCII letters, numbers when meaningful, and hyphens: #cancel-subscription is portable; #Section 4! is not.
Design examples
Every variant uses the same link-to-ID contract. Variants differ by context and density, not by inventing new destination behavior.
Compact inline: use three to six peer destinations near the opening. Allow wrapping and keep separators out of accessible link names.
Stacked list: use five to twelve destinations when labels need room or scanning matters more than vertical space. This is the default for long guides and policies.
Nested list: use H2 destinations as the main route and include only substantial H3 children. Limit depth to two navigation levels; deeper trees belong in documentation navigation.
Local index: use letters, categories, or reference groups to navigate one bounded region. Render destinations that do not exist as plain disabled-looking text, not links with empty or fake targets.
Heading permalink: provide a small copy-link control beside a heading when readers commonly cite individual sections. Its accessible name must include the heading, such as “Copy link to Renewal terms.” The heading itself remains text, not one large self-link.
Inline cross-reference: use a normal sentence link when one passage depends on another section. Prefer “review the renewal terms” to “jump here.”
Parameters
The collection and each destination have separate fields. The first heading in a portable directive body maps to the navigation label under the default body rule; subsequent list items supply the links.
| Name | Type | Required | Min/max | Default | Source |
|---|---|---|---|---|---|
label | Plain string | Yes for a group | 2–6 words; 60 characters maximum | On this page | Attribute or first heading in body |
variant | Enum | No | inline, stacked, nested, local-index, permalink | stacked | Attribute |
items | Link collection | Yes for a group | 3–12 visible items; 26 allowed for an A–Z index | None | Body list |
text | Plain inline text | Yes per item | 2–10 words; 70 characters maximum | Destination heading text | Body link label |
href | Fragment URL | Yes per item | One #id; no empty fragment | Derived from targetId | Body link destination or item attribute |
targetId | Unique HTML ID | Yes | 1–8 lowercase hyphenated tokens | Generated from heading text before first publication, then pinned | Destination heading attribute or editor anchor field |
depth | Integer enum | No | 1 or 2 | 1 | Attribute; may be derived from body nesting |
copyable | Boolean | No | true or false | false | Attribute |
content | Markdown link list | Yes for group variants | One list matching items; no prose-only body | Everything after the first body heading | Body |
The editorial item target is intentionally smaller than the technical maximum. If a page needs 18 destinations, first group related sections, use a generated table of contents, or divide the document. An A–Z index is the narrow exception because its order and labels are already predictable.
Syntax and code examples
The portable source stores link text and fragments explicitly so it can move between renderers without losing published IDs.
Portable Markdown directive
:::anchor-links{variant=stacked depth=1}
### On this page
- [Eligibility](#eligibility)
- [Required documents](#required-documents)
- [Renewal terms](#renewal-terms)
:::
## Eligibility {#eligibility}
The first body heading becomes label; the list becomes content and supplies items. Pin the destination ID when the page is first published.
Hugo shortcode
{{< anchor-links variant="stacked" label="On this page" >}}
- [Eligibility](#eligibility)
- [Required documents](#required-documents)
- [Renewal terms](#renewal-terms)
{{< /anchor-links >}}
## Eligibility {#eligibility}
This is the Hugo adapter contract, not a claim that this repository already registers an anchor-links shortcode. A Hugo implementation may generate the list from .TableOfContents or render ordinary Markdown links, provided it preserves the same IDs, semantics, limits, and accessibility behavior.
WordPress
<!-- wp:group {"tagName":"nav","ariaLabel":"On this page"} -->
<nav aria-label="On this page">
<ul>
<li><a href="#eligibility">Eligibility</a></li>
<li><a href="#required-documents">Required documents</a></li>
<li><a href="#renewal-terms">Renewal terms</a></li>
</ul>
</nav>
<!-- /wp:group -->
<!-- wp:heading {"level":2,"anchor":"eligibility"} -->
<h2 id="eligibility">Eligibility</h2>
<!-- /wp:heading -->
Set WordPress’s Advanced → HTML anchor field explicitly for published destinations. Do not rely on a theme or plugin to regenerate IDs from changed visible text.
Examples
Good: labels and IDs survive editorial improvement
**On this page**
- [Calculate the total cost](#calculate-total-cost)
- [Compare contract terms](#compare-contract-terms)
- [Choose a plan](#choose-plan)
## Compare annual and monthly contract terms {#compare-contract-terms}
This works because the navigation labels predict distinct answers, the ID is readable and unique, and the heading can become more specific without changing the published #compare-contract-terms address. A citation created before the wording improvement still reaches the correct section.
Bad: generated IDs are treated as disposable
- [More information](#more-information)
- [Click here](#section-4)
- [Pricing](#pricing-2026)
## New pricing details
This fails because two labels need surrounding context, section-4 encodes position rather than meaning, and pricing-2026 will become misleading when the year changes. The heading has also changed without retaining the old id="pricing-2026", so existing inbound fragments no longer resolve. Renaming the list entry cannot repair links already published elsewhere.
Schema markup and accessibility
Anchor links do not feed a dedicated Schema.org type. Do not mark a jump-link list as ItemList merely because it uses <ul>; its purpose is navigation, not a ranked or curated collection of entities. A destination section may contribute visible content to Article, FAQPage, HowTo, or another appropriate page-level schema type, but the fragment itself is not separate structured data.
Use native <a href="#target-id"> links. Wrap a page-level or local group in <nav aria-label="On this page"> when it is a distinct navigation region. Do not add role="link" to a native anchor. Each target ID must be unique, and the href must match it exactly, including case.
Keyboard users need a visible focus indicator. Touch targets need enough spacing to avoid accidental activation. Link text must identify the destination without relying on color, nearby prose, or a title attribute. If a copy-permalink control uses a button, announce success without moving focus unexpectedly.
Fragment navigation must leave the target heading visible below sticky headers. Prefer CSS scroll-margin-top on targets. Smooth scrolling is optional and must respect reduced-motion preferences. Avoid JavaScript that cancels native link behavior, removes the fragment from browser history, or changes scroll position without updating the URL.
Do not automatically move focus for every ordinary fragment click; native browser behavior should remain predictable. If a custom menu or disclosure closes after activation and focus would otherwise be lost, move focus deliberately to a target that can receive it, such as the heading with tabindex="-1", then test with keyboard and screen-reader navigation. A persistent navigation control may indicate the current destination with aria-current="location", but only when the state is updated accurately.
Writing rules
Write destinations after the page outline is stable and before links circulate. Use sentence case and concrete nouns or verbs. A label should make sense in isolation because copied links, screen-reader link lists, and machine citations may remove it from its original visual context.
- Use three to twelve links in a visible group; prefer five to eight for a compact opening set.
- Keep labels to two to ten words and under 70 characters.
- Include H2 destinations by default. Include H3 only when it answers an independent need.
- Keep one grammatical pattern within a group: all questions, all noun phrases, or all imperative verbs.
- Use IDs of one to eight lowercase, hyphen-separated tokens. Begin with a letter and omit punctuation, emoji, diacritics, dates that will expire, and position numbers.
- Freeze every public ID. A visible heading can change without changing its ID when the section’s purpose remains the same.
- Never reuse a retired ID for unrelated content. An old link must not appear to work while landing on a different claim.
- Never put citations, footnote markers, prices, promotional badges, or urgency claims in a navigation label.
- Never place buttons, form controls, images, or another link inside an anchor link.
- Never use “click here,” “read more,” “details,” or “section” as the complete label.
When a section is removed, decide what the old fragment promised. If its content moved to an equivalent section on the same page, preserve the old ID as an alias adjacent to the new destination where the platform supports multiple anchors. If no equivalent exists, do not silently attach the old ID to a different section. Record the removal and update every known internal reference.
Post types that use it
The postTypes frontmatter array drives this relationship. Inclusion means the format commonly benefits from stable section destinations; it does not require a visible jump list on every short instance.
| Post type | Why it uses anchor links | Typical destinations |
|---|---|---|
| Ultimate guides | Broad coverage creates several legitimate entry points and citation targets. | Major subject areas, methods, limitations, next steps |
| How-to guides | Readers revisit prerequisites, phases, verification, and recovery paths. | Prerequisites, phases, troubleshooting, verification |
| Documentation articles | Support and product interfaces often link directly to one maintained instruction. | Configuration groups, fields, errors, examples |
| FAQ hubs | Visitors arrive with one question and need a short route to its answer group. | Question categories and substantial individual answers |
| Benchmark reports | Findings must remain connected to method, population, and limitations when shared. | Methodology, cohorts, findings, limitations |
| Policy pages | Readers and teams cite individual rules, exceptions, and responsibilities. | Scope, obligations, exceptions, effective dates |
| Directory indexes | Large predictable collections benefit from alphabetical or category jumps. | Letters, categories, regions, entity groups |
QA checklist
- The page has a real non-linear navigation need; the list is not decoration.
- Every visible link label describes its destination when read alone.
- Every href begins with
#for a same-page link and matches one existing target ID exactly. - Every target ID is unique, lowercase, readable, and free from transient dates or position numbers.
- Published IDs have been compared with the previous release and remain unchanged unless a migration is documented.
- A visible heading rename preserves its old ID when the section purpose is unchanged.
- Removed or merged destinations do not reuse old IDs for different content.
- The group contains 3–12 useful items, except a justified A–Z local index.
- H3 destinations appear only when they are substantial and correctly nested under an H2.
- The group is not duplicated by a table of contents or adjacent navigation component.
- The navigation does not interrupt a heading and its answer, a claim and its evidence, or a required sequence.
- The group uses native links and, when distinct, a named navigation landmark.
- Keyboard focus is visible and follows a logical order.
- The target heading remains visible below sticky headers at desktop and mobile widths.
- Reduced-motion settings are respected when smooth scrolling is present.
- Copy-link controls name the destination and announce success without losing focus.
- Directly loading each full URL plus fragment lands on the intended section after a fresh page load.
- Known inbound internal links, bookmarks used in support material, and cited fragment URLs have been regression-tested after edits.
Reject publication when the page itself loads but a known fragment no longer resolves. A broken anchor is a broken inbound link at section level, even when page-level monitoring reports a successful response.
FAQ
The questions above cover the maintenance decisions that most often break anchor navigation after publication. Their authoritative answers live in the structured [[faq]] frontmatter so visible FAQ output and any eligible schema output can use the same source.
More tutorials in this section
Ready to put it into practice?
Free check · 7-day trial · no credit card