Skip to main content

Linking and Navigation inside the Markdown Macro

A long page inside an Enhanced Markdown macro needs a way in: a contents list at the top, a "back to top" link, or a cross-reference from one section to another. This page explains how in-page links work inside the macro, how to build a table of contents, and the few heading shapes that need extra care.

Version

In-page anchor navigation was improved in v1.4.1 (2026-10-10). If a link below does nothing on your page, update the macro to the latest version and save it once.


The Short Version​

Link to a heading with a plain # anchor, using the heading text lowercased with spaces replaced by hyphens:

Jump to [Getting Started](#getting-started)

Clicking it scrolls the page so that heading sits just below Confluence's sticky toolbar. No page reload, and the browser back button still works.

Three rules cover most cases:

  1. The anchor is the heading text, lowercased, spaces to hyphens. #Getting Started does not work — write #getting-started.
  2. Targets must be headings in the same macro. A link cannot jump to a heading in normal page content, a different macro, or another page.
  3. Punctuation is dropped, not turned into hyphens. ## API v2.0 (Beta) becomes #api-v20-beta — see Punctuation disappears.

How Heading Anchors Are Generated​

The macro reads each heading's text and derives its anchor automatically — you never write anchor ids by hand. Understanding the exact rule is what makes the awkward cases predictable.

The rule runs in four steps:

  1. Take the heading's visible text, with inline formatting markers removed
  2. Lowercase it
  3. Remove every character that is not a letter, digit, whitespace, or hyphen
  4. Collapse each run of whitespace into a single hyphen

Note that step 3 is Unicode-aware. Letters and digits from any script survive — CJK, Cyrillic, accented Latin, and so on all work as anchors. This is why a heading written in Chinese produces a usable anchor rather than an empty one:

Heading in the sourceAnchor to link to
## 系统要求#系统要求
## Configuration#configuration
## Ünïcödé Tëst#ünïcödé-tëst

If a heading contains nothing but punctuation, it falls back to the anchor section. Give such headings real words instead.


Worked Examples​

A table of contents​

The most common reason to link is a contents list. Write it as an ordinary list of links near the top of the macro, and anchor each item to the heading below it:

## Contents

- [Overview](#overview)
- [Installing the client](#installing-the-client)
- [Configuring the server](#configuring-the-server)
- [Troubleshooting](#troubleshooting)

## Overview

...

## Installing the client

...

## Configuring the server

...

## Troubleshooting

...

Each list item is just a normal Markdown link. Keep the link text identical to the heading so the two stay easy to keep in sync as the document grows.

The same mechanism in reverse. Add an inline link wherever a reader might want to return to the contents:

## Configuring the server

...long section...

[↑ Back to contents](#contents)

For this to work, the contents heading itself must exist — ## Contents gives you #contents. Anchoring to a heading you have not written is the most common cause of a link that silently does nothing.

Cross-referencing another section​

Link the readable way, using the section's own heading text:

If authentication fails, see [Configuring the server](#configuring-the-server).

Prefer this over an invented anchor name like #server-config. If you later rename the heading, a link written from the heading text is the one a reader can still reason about, and it fails loudly in review rather than silently at click time.


Punctuation Disappears (Why Some Anchors Look Wrong)​

Because step 3 removes punctuation instead of converting it, some anchors are not what you would guess. These are the shapes that catch people out:

HeadingAnchorNote
## Getting Started#getting-startedThe normal case
## Getting Started!#getting-startedTrailing ! dropped
## What's New?#whats-newApostrophe dropped, no hyphen
## Step 1: Install#step-1-installColon dropped, space becomes the hyphen
## API v2.0 (Beta)#api-v20-betaThe . vanishes — it is v20, not v2-0
## Deploy & Rollback#deploy-rollbackStandalone & joins the words with no hyphen
## config.yaml#configyamlDots dropped, so yaml runs into config

The reliable habit is to derive the anchor from the heading text mechanically — lowercase it and put hyphens where the spaces were — rather than guessing. For a heading with awkward punctuation, check the link once in the rendered page before you rely on it.

If you want anchors that survive punctuation exactly, simplify the heading text. ## API v2.0 (Beta) and ## API v2 Beta are both fine; the second gives a predictable #api-v2-beta.


Duplicate Headings​

Two headings with the same text cannot share an anchor. The macro keeps them distinct by appending a counter, in document order:

## Notes

...first...

## Notes

...second...

The first is #notes and the second is #notes-1. A third would be #notes-2.

This makes duplicate headings safe but unpredictable to link to — a reader cannot tell which "Notes" they will land on, and inserting a section above silently renumbers them. Where possible, give repeated sections distinct headings (## Notes for macOS, ## Notes for Windows) and link each one directly.


Limits Worth Knowing​

Anchors reach headings inside the same macro only. The macro renders inside its own frame, so its list of anchors covers only the headings it rendered. An anchor pointing at a heading in normal page content, in a second Markdown macro, or in a table elsewhere on the page will not resolve.

If you need to reference something outside the macro, link to the page rather than the heading, or move that content inside the macro.

Cross-page links open in a new tab. A link to another Confluence page opens in its own tab, controlled by Confluence. This is deliberate: it prevents a link in a rendered document from silently navigating a reader away from a page they may have been reading for a while.

Anchors are assigned on save, not on the host page. The anchors belong to the macro's rendered output, so the set is determined by the macro's own headings. Editing the surrounding page does not change them.


Troubleshooting​

A link does nothing when clicked.

Check, in order:

  1. Does the target heading exist in the same macro? This is the most common cause. An anchor with no matching heading is simply ignored.
  2. Is the anchor lowercased? Headings render with capital letters, but anchors are matched in lowercase. [X](#Getting-Started) fails; [X](#getting-started) works.
  3. Is the anchor derived from the heading text? Re-derive it: lowercase the heading, spaces to hyphens, punctuation removed. If the heading contains punctuation, verify against the table above.
  4. Is the macro on v1.4.1 or later? Older versions did not assign anchors at all. Update and save the macro once.

The jump lands on the wrong section.

Almost always a duplicate heading. Check for repeated headings — the second one is #notes-1, not #notes. Rename them to tell them apart.

The heading is hidden behind the toolbar.

It should not be — the macro offsets the scroll so the target clears Confluence's sticky toolbar. If it is still obscured on your page, confirm the macro is fully rendered (scroll through it once) and try again.

An anchor works in the editor preview but not on the page.

Save the macro. Anchors are assigned when the content is rendered and saved, so an unsaved edit will not have them yet.


Support​

For issues or feature requests, please visit the app's listing on the Atlassian Marketplace.

Was this helpful?