Note
Do I need this? Yes when linking pages in Markdown content. For Kida template functions and path resolution details, see Linking Reference.
Bengal provides multiple ways to create links between pages, headings, and external resources. Choose the method that best fits your use case.
Tip
Try it out: This guide includes a test target anchor that demonstrates arbitrary reference targets. Scroll down to see it in action!
Quick Reference
| Method | Use Case | Syntax |
|---|---|---|
| Markdown Links | Standard links, external URLs | [text](url) |
| Cross-References | Internal page links with auto-title | [[path]] |
| External References | Link to external docs (Python, NumPy, etc.) | [[ext:project:target]] |
| Template Functions | Dynamic links in templates | {{ ref('path') }} |
| Anchor Links | Link to headings | [[#heading]] or #heading |
| Target Directives | Arbitrary anchors mid-page | :::{target} id |
| Target References | Link to target directives | [[!target-id]] |
Standard Markdown Links
Use standard Markdown link syntax for external URLs and simple internal links.
External Links
[Visit GitHub](https://github.com)
[Email us](mailto:hello@example.com)
Internal Links
Absolute paths (from site root):
[Get Started](/docs/get-started/)
[API Reference](/docs/reference/api/)
Relative paths (from current page):
<!-- From docs/get-started/installation.md -->
[Quickstart](./quickstart.md)
[Configuration](../reference/config.md)
[Home](../../_index.md)
Path Resolution Rules:
.mdextension is optional — Bengal resolves bothpage.mdandpage- Relative paths resolve from the current page's directory
./refers to current directory,../goes up one level- Paths are normalized automatically (trailing slashes, etc.)
Link with Title
[Link text](https://example.com "Optional title")
Cross-References ([[link]]Syntax)
Cross-references provide intelligent linking with automatic title resolution and O(1) lookup performance. Use them for internal page links.
Basic Cross-Reference
[[docs/getting-started]]
This automatically:
- Resolves to the page at
docs/getting-started.md - Uses the page's title as link text
- Handles path normalization automatically
Custom Link Text
[[docs/getting-started|Get Started Guide]]
The |separator lets you specify custom link text while still using intelligent path resolution.
Link by Custom ID
If a page has a customidin frontmatter:
---
id: install-guide
title: Installation Guide
---
Link to it by ID:
[[id:install-guide]]
[[id:install-guide|Installation]]
Benefits of ID-based links:
- Stable even if page path changes
- Shorter syntax
- Works across site restructures
Link to Headings
Link to any heading in your site:
[[#installation]]
[[#configuration]]
This finds the heading text (case-insensitive) and links to it. If multiple pages have the same heading, it uses the first match.
Link to heading in specific page:
[[docs/getting-started#installation]]
Path Resolution
Cross-references support multiple path formats:
[[docs/getting-started]] # Path without extension
[[docs/getting-started.md]] # Path with extension (also works)
[[getting-started]] # Slug (if unique)
[[id:install-guide]] # Custom ID
Resolution order:
- Custom ID (
id:xxx) - Path lookup (
docs/page) - Slug lookup (
page-name)
Broken References
If a cross-reference can't be resolved, Bengal shows a broken reference indicator:
[[nonexistent-page]]
Renders as: [nonexistent-page]with a visual indicator for debugging.
Anchor Links
Link to headings within the same page or across pages.
Same-Page Anchors
[Installation](#installation)
[Configuration](#configuration)
Headings automatically get anchor IDs based on their text. For example:
## Installation
This heading gets ID: `installation`
Custom Anchor IDs
Use{#custom-id}syntax for custom anchor IDs on headings:
## Installation {#install-guide}
Link to it: [[#install-guide]]
Arbitrary Reference Targets
Create anchor targets anywhere in content (not just on headings) using the{target} directive. This is similar to RST's .. _label:syntax.
Syntax:
:::{target} my-anchor-id
:::
Example: Target Directive Usage
:::{target} important-caveat
:::
:::{warning}
This caveat is critical for production use.
:::
See [[!important-caveat|the caveat]] for details.
Reference Syntax:
Use[[!target-id]]to explicitly reference target directives:
[[!test-target]] # Link with auto-generated text
[[!test-target|Custom Text]] # Link with custom text
Why ! instead of #?
The!prefix distinguishes target directive references from heading anchor references:
[[#heading]]- References heading anchors (auto-generated or custom{#id})[[!target-id]]- References target directives (explicit:::{target})
This eliminates collisions and makes your intent explicit.
Use cases:
- Anchor before a note/warning that users should link to
- Stable anchor that survives heading text changes
- Anchor in middle of content (not tied to heading)
- Migration from Sphinx (
.. _label:) or RST reference targets
Anchor ID requirements:
- Must start with a letter (a-z, A-Z)
- May contain letters, numbers, hyphens, underscores
- Case-sensitive in output, case-insensitive for resolution
Note: The target renders as an invisible anchor element. Any content inside the directive is ignored (targets are point anchors, not containers).
Try it: This page has a test target below. Jump to it: Test Target
This is a test target anchor. You can link to it using[[!test-target]]from anywhere in your site.
Cross-Page Anchors
[Installation](/docs/getting-started/#installation)
[[docs/getting-started#installation]]
Both syntaxes work. Cross-references are preferred for internal links.
See Also
- Linking Reference — template functions and path resolution
- External References — Python stdlib, NumPy, cross-site links
- Content Validation — link validation with
bengal check