skip to content

guides / markdown-wikilinks-terminal

Markdown wikilinks in the terminal: resolution, backlinks, renames

How [[wikilinks]] resolve by filename, title or id in Obsidian, vimwiki, Logseq, Zettlr, Dendron and tnotes; backlinks, rename handling, and ripgrep recipes.

published 2026-09-22

A wikilink is two brackets around a name: [[Weekly plan]]. Most tools also accept [[Target|shown text]] for a display label and [[Target#Heading]] for an anchor. It is not part of CommonMark or GitHub Flavored Markdown, so a plain Markdown renderer prints the brackets literally. The syntax is shared; what the name inside the brackets refers to is not. That is where tools disagree, and it is what breaks when you move a vault from one tool to another.

This guide covers the three resolution strategies in use, how backlinks are computed, what happens when the target is renamed, and how to answer link questions in a bare folder with rg when no tool is running.

What the name resolves to

There are three answers, and a vault written for one is subtly wrong under another.

By filename

The link text is a file path without the extension. [[Weekly plan]] means Weekly plan.md, somewhere in the vault.

Obsidian works this way: [[Three laws of motion]] and [[Three laws of motion.md]] are equivalent, a folder prefix such as [[Projects/Three laws of motion]] is a path from the vault root, and a link to a missing note creates the file at that path (Obsidian help: Internal links). The note’s first heading is irrelevant; the filename is the identity.

vimwiki is the same model with Vim path rules: [[projects/Important Project 1]] is relative to the current file’s wiki, [[/index]] is absolute to the wiki root, [[../index]] walks up, and wiki1: or wn.Name: prefixes address another registered wiki (vimwiki documentation, section 5.2). Default extension is .wiki; .md is a per-wiki setting.

Dendron is filename-based with a twist: dots in the filename form a hierarchy, so project1.designs.promotion.md is the note promotion under project1.designs, and the link is [[project1.designs.promotion]] (Dendron: Concepts). Folders are not used for structure at all.

Consequence: the filename must be human-readable, since it is what you type inside the brackets, and spaces in filenames are normal. Anything that slugifies filenames (weekly-plan.md) is incompatible with links written for this model.

By title

The link text is the note’s title, a value inside the file, and the filename is derived from it or independent of it.

Logseq resolves [[Brackets]] and #Hashtags to a page by name, and states the two forms function the same; a link to a page that does not exist creates it (Logseq docs: How to create pages). Page names are matched without regard to case in [[...]] references, while the (page "…") query form is case-sensitive (logseq/logseq#4004).

nb accepts either a title or an id inside the brackets: [[Example Title]], [[123]], [[Sample Folder/Demo Title]], and across notebooks [[demo:Example Title]] or [[sample:Example File.md]] (xwmx/nb: Linking).

tnotes resolves by title first, case-insensitively, then falls back to the file stem; details in the tnotes section below.

Consequence: the title is inside the file, so the tool has to index every file’s first line (or frontmatter) before it can resolve anything. In exchange the filename can be a slug, and case in the link does not matter.

By id

The link text is an opaque identifier that never changes. The note’s title and filename are free to change around it.

Zettlr’s default id is a timestamp in the form YYYYMMDDHHMMSS, and the manual’s argument is exactly the rename problem: “the title of the note can change, but the ID remains the same.” A setting, “Use the file ID as link target if possible”, makes autocomplete insert [[20250922141500|Weekly plan]] rather than the filename (Zettlr manual: Zettelkasten methods). Zettlr can also link by filename, so a single vault can mix both.

nb’s numeric ids ([[123]]) are the same idea without the timestamp.

Consequence: links never break on rename, and never read well in a plain text editor. Ids are the right choice for a vault that will outlive several tools, at the cost of every link needing a label to be legible. See Zettelkasten for the method this comes from.

A backlink is the reverse edge: every note that contains a link to the current one. No Markdown syntax expresses it; it is computed.

Two designs exist. Most tools keep an in-memory or on-disk index and show backlinks in a pane or overlay; nothing is written into the note. Obsidian, Logseq, Dendron and tnotes do this. tnotes additionally exposes the result as data: tnotes ls --json includes links and backlinks arrays for every note, so a script can walk the graph without the TUI (tnotes README).

The other design writes backlinks into the file. nb’s backlink.nb-plugin crawls a notebook and appends a “Backlinks” section to each linked note listing the passages that reference it (nb plugin source). vimwiki’s :VimwikiBacklinks populates a search list instead of editing files.

Written-in backlinks survive tool changes and show up in any editor; they also drift if you forget to rerun the crawler, and they make every note contain text you did not write. Indexed backlinks are always current and never touch the file, and vanish when you leave the tool.

What happens on rename

This is the question to ask before committing to a tool. The answers:

Tool Resolution On rename
Obsidian filename updates internal links automatically; can be switched to a prompt under Settings, Files and links (source)
vimwiki filename :VimwikiRenameFile renames the page and rewrites links to it, including diary pages; a plain mv does not (source)
Dendron dot-hierarchy name Rename Note updates all backlinks; Refactor Hierarchy does the same over a regex-matched set with a preview (source)
Logseq page name references are updated; open issues track gaps for org-format aliases and {{query}} blocks (logseq/logseq#7519)
Zettlr id or filename id links are unaffected by rename; the manual recommends the id setting precisely because filename links break when a file is renamed (source)
tnotes title, then stem changing a title rewrites [[old title]] to [[new title]] in every other note, including open unsaved tabs; the file is renamed to the new slug (source)

Two things the table hides. First, “rename” means “through the tool”. Rename a file with mv or in Finder and every filename-based tool now has a dangling link. Second, rewriting links is a mass edit across the vault, which is exactly what a syncer running on another machine may also be touching; a rename while another device has unsynced edits is the most reliable way to produce a conflict copy. See Syncing plain-text notes.

None of the above requires a tool to be running. With rg you can answer the common questions over any Markdown folder.

Every note that links to a given title:

rg -l --fixed-strings '[[Weekly plan' ~/notes

The --fixed-strings avoids escaping the brackets. Dropping the closing ]] also catches [[Weekly plan|label]] and [[Weekly plan#Heading]].

Every link target in the vault, deduplicated with counts:

rg -o --no-filename '\[\[([^\]|#]+)' -r '$1' ~/notes | sort | uniq -c | sort -rn

Broken links, for a filename-resolved vault where the target must exist as <name>.md:

cd ~/notes
rg -o --no-filename '\[\[([^\]|#]+)' -r '$1' . | sort -u > /tmp/targets
fd -e md . | sed 's|^\./||; s|\.md$||' | sort -u > /tmp/files
comm -23 /tmp/targets /tmp/files

Notes nobody links to (orphans), same assumption:

comm -13 /tmp/targets /tmp/files

For a title-resolved vault, replace the fd line with one that extracts the first heading: rg -m1 --no-filename '^# (.*)' -r '$1' ., and compare case-insensitively with sort -uf and comm -i.

Backlinks for the note you have open in any editor, as a one-liner to bind to a key:

rg -n --fixed-strings "[[$(head -1 current.md | sed 's/^# //')" ~/notes

These are approximate. They do not know about links inside fenced code blocks, escaped brackets, or aliases. For a few thousand notes they are fast enough that precision is rarely worth the tooling.

From the README and changelog, and only that:

The stem fallback means a link written for a filename-resolved vault ([[weekly-plan]]) still works if the slug matches, and a link written for a title-resolved vault ([[Weekly Plan]]) works regardless of case. Nested folder paths inside the brackets and #heading anchors are not mentioned in the README, so do not assume them. Keys are listed in /docs/keys; the CLI fields in /docs/cli.

If the vault may ever be opened by a different tool, a few habits keep the links resolvable:

For side-by-side detail on specific tools, see tnotes vs Obsidian, tnotes vs Zettlr, and tnotes vs Dendron.

More guides