How to link to part of the same document in Markdown?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
To link to another section within the same Markdown document, use the standard link syntax with a # anchor derived from the heading text: [link text](#heading-slug). The slug is created by converting the heading to lowercase, replacing spaces with hyphens, and removing special characters. This is the foundation of every Table of Contents in Markdown and works across GitHub, GitLab, VS Code previews, and most static site generators.
Basic Syntax
Every heading in a Markdown document automatically becomes an anchor target. You link to it by constructing a slug from the heading text.
The anchor #configuration-options is derived from the heading ## Configuration Options by lowercasing and replacing the space with a hyphen.
Slug Generation Rules
The exact rules vary slightly between Markdown processors, but the most common convention (used by GitHub, GitLab, and most static site generators) is:
- Convert the heading text to lowercase
- Replace spaces with hyphens (
-) - Remove all punctuation except hyphens
- Collapse multiple consecutive hyphens into one
Processor-Specific Differences
Not all processors follow the same rules. Here is how common platforms handle edge cases:
| Platform | Punctuation handling | Duplicate headings | Emoji in headings |
| GitHub Flavored Markdown | Removes most punctuation, keeps hyphens | Appends -1, -2, etc. | Strips emoji |
| GitLab | Same as GitHub | Appends -1, -2, etc. | Strips emoji |
| Hugo (Goldmark) | Removes punctuation, keeps hyphens | Appends -1, -2, etc. | Strips emoji |
| Docusaurus | Removes punctuation | Appends -1, -2, etc. | Strips emoji |
| Pandoc | Removes punctuation, prefix with section- optional | Appends -1, -2, etc. | Keeps emoji in ID |
| VS Code preview | Follows CommonMark extensions | Does not deduplicate | Strips emoji |
When in doubt, check the rendered HTML and inspect the id attribute on the heading element.
Manual Anchors with HTML
When the auto-generated slug is inconvenient or you need an anchor at a location other than a heading, you can insert a raw HTML anchor.
This is particularly useful for:
- Linking to a specific paragraph, not just a heading
- Creating short, stable anchor names that do not change when you rename a heading
- Linking to positions inside code blocks or tables
Some Markdown processors also support the heading ID syntax with curly braces:
This works in Pandoc, Hugo, Docusaurus, and several other processors, but not in GitHub Flavored Markdown.
Building a Table of Contents
A manual table of contents is just a list of internal links at the top of the document.
Many tools can auto-generate a TOC. For example, VS Code has Markdown All in One extension, and many static site generators have TOC plugins. But the manual approach gives you full control over ordering and nesting.
Linking Across Files
While not strictly "same-document" linking, you can combine file paths with anchors to link to specific sections in other Markdown files.
On GitHub, these links work in repository browsing. In static site generators, the file extension is usually replaced with .html or removed entirely depending on the URL scheme.
Practical Example: A Full Document
Notice how the internal links create a navigable web within a single document, letting readers jump directly to relevant sections.
Common Pitfalls
- Forgetting to lowercase the anchor. Markdown anchors are case-insensitive in some processors but case-sensitive in others. Always use lowercase in your links to be safe:
[Link](#my-heading), not[Link](#My-Heading). - Including punctuation in the anchor. A heading like
## What's New?becomes#whats-new, not#what's-new?. Apostrophes, question marks, periods, and colons are stripped. - Duplicate headings. If you have two
## Overviewheadings, the second one becomes#overview-1. This is fragile because inserting a new heading can shift the numbering. Use unique heading text or manual anchors. - Spaces in anchors. Anchors use hyphens, not spaces or
%20. Writing[Link](#my heading)will not work. - Relying on curly-brace IDs on GitHub. The
{#custom-id}syntax is not supported on GitHub. Use<a id="custom-id"></a>instead if you need custom anchors on GitHub. - Not testing links after renaming headings. When you rename a heading, every internal link pointing to it breaks silently. Search your document for the old anchor text after any heading change.
Summary
Internal links in Markdown use the syntax [text](#anchor) where the anchor is derived from the heading text by lowercasing, replacing spaces with hyphens, and stripping punctuation. For custom anchor positions, use <a id="name"></a> in HTML. For processors that support it, the {#id} attribute syntax provides cleaner custom anchors. Always verify your slugs match the processor's rules, keep headings unique to avoid numbered suffixes, and search for stale anchors whenever you rename a heading. These links are the backbone of readable, navigable documentation.
Related reading
- How to list npm user-installed packages?
- How to list the properties of a JavaScript object?
- How to listen for a WebView finishing loading a URL?
- How to load an image asynchronously?
- How to load CSS Asynchronously
- How to load local html file into UIWebView
- How to load npm modules in AWS Lambda?
- How to make a function wait until a callback has been called using node.js
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.