Manage Cross-Site Links
When your project hosts more than one Docusaurus instance under the same
domain (for example a User area at /docs/ and a Developer area at
/developers/), links that cross instance boundaries must use URL links
(root-relative /developers/… or a full URL) instead of relative Markdown
file paths. Docusaurus does not resolve relative file-path links across plugin
boundaries — and neither does Zenzic's link validator.
By default, Zenzic's Z105 ABSOLUTE_PATH rule rejects any absolute link
(/foo/bar) because absolute paths break when a site is hosted in a
subdirectory. This guide shows you how to declare the cross-instance
prefixes your project legitimately owns, so the validator stops flagging
them — without weakening Z105 elsewhere.
TL;DR — Which tool, when?
| Situation | Use this | Don't use |
|---|---|---|
| One isolated line in one file legitimately matches a rule | <!-- zenzic:ignore: Zxxx --> (or {/* zenzic:ignore: Zxxx */} for MDX) | — |
| Multiple cross-plugin links in different files | Inline ignores — one per link | — |
The decision rule: if it is a property of one line, it belongs inline.
Cross-Instance Prefix Handling
[link_validation] removedThe [link_validation] TOML schema — including absolute_path_allowlist — is unsupported and raises a TOML validation error at startup. A .zenzic.toml that still declares [link_validation] must be updated.
For cross-instance links that Z105 flags, use inline ignores at each affected line.
When to use an inline ignore instead
Inline ignores are surgical. Reach for them when:
- A single line in a single file legitimately triggers a rule (e.g. a documentation example that looks like a credential but is fake).
- The exception is local context, not a project-wide truth.
<!-- zenzic:ignore: Z2XX -->
api_key = "sk_test_PLACEHOLDER_FOR_DOCS"
{/* zenzic:ignore: Z1XX */}
[Hard link example](/legacy/path)
The inline form leaves an audit trail at the exact line — visible in PR
diffs, traceable in git blame.
Anti-pattern: over-using inline ignores
Do not add <!-- zenzic:ignore: Z1XX --> as a blanket suppression. This:
- Implies the link is "broken and accepted" when in reality it is correct by design.
- Hides the cross-instance dependency from PR reviewers.
Annotate inline ignores with a comment explaining why the link is legitimately absolute,
so the suppression is traceable in git blame.
Reverting
Remove an inline ignore and Z105 enforcement returns immediately on that line. There is no migration cost.
Managing Virtual Identity: Slug-Based Routes
Docusaurus lets any page declare a custom slug: in its frontmatter that
completely overrides the filename-derived URL. Once a slug is declared,
only the slug URL exists in Docusaurus's routing table — the original
filename path is gone.
Zenzic enforces this identity at validation time. When the Docusaurus adapter
is active, set_slug_map() reads frontmatter slugs before building the
Virtual Site Map (VSM). The VSM then contains only the canonical slug URL —
not the filename URL.
Identifying the correct route
Given a post with the following frontmatter:
---
slug: sample-post
title: A Sample Post
---
| Link | Result |
|---|---|
[Post](/blog/2026-05-05-post) | ❌ Z104 — filename-derived path absent from the VSM |
[Post](/blog/sample-post) | ✅ Validated — slug matches the VSM entry |
The validator raises Z104 FILE_NOT_FOUND (not Z105) because /blog/ is a
project-owned prefix. The prefix is trusted; the exact route must still exist.
Finding the canonical slug
Run zenzic inspect routes --kind physical to list every route in the VSM
alongside its source file and frontmatter slug:
$ zenzic inspect routes --kind physical
/blog/sample-post/ ← blog/2026-05-05-post.md [slug: sample-post]
Update any link that points to the old filename-derived path to use the slug URL shown in the output.
Engine agnosticism
Slug-map resolution is adapter-selective. Simpler adapters (Standalone, MkDocs) are unaffected — the slug-map path is a Docusaurus-only capability. No configuration change is needed when switching adapters.
Related
- Suppression Policy — full inline- ignore syntax and scope rules.