We’re running a fairly large Grav website with lots of content. Our editors regularly write and edit pages in Markdown, and often create internal links like this:
[Contact Form](/contact)
The issue we’re facing:
Whenever a page gets renamed or moved (e.g. changing the folder name or slug or folder position), these internal links break — which is expected, since Grav doesn’t automatically update or manage those references.
We’re currently looking for the cleanest and most robust way to:
Prevent broken internal links when pages are renamed or moved
Make it easy for editors to insert stable internal links
Avoid completely refactoring the existing site structure (retroactively adding UUIDs everywhere isn’t realistic)
What we already use or know about:
route_override for important permanent URLs
redirect in the page frontmatter to forward old URLs
Manual search & replace using a code editor or shell script
What we’re still missing:
Some kind of internal linking system (shortcode or plugin) that’s also retrofit-friendly for existing content
Or at least a tool to check all Markdown files for broken internal links
Our questions:
How do you deal with this in larger Grav sites?
Are there any tools, plugins, or best practices to manage or update internal links across content?
Has anyone implemented a reliable system for internal linking that survives content restructuring?
Any suggestions or experience would be super helpful. Thanks in advance!
Firstly i should say i dont know about big sites or if this will help.
Here is an easy way to do things imo.
You have many pages and they may or may not have internal links.
Some of them changes over time and you have hard time to follow that.
To do that you already tried to search and replace etc. but that requires some time and effort for 100k pages.
what about this ?
add links to frontmater like
links:
link1: abc
link2: ccc
and when you need them on content use {{ page.header.link2 }}
later you change a page’s url.
thinking “i wonder if i had this url as internal link for another page.”
you just need to create a page that lists all links from frontmater and filter them.
i know using twig may not look good for writers but that s what i could think.
i hope this helps
Most changes are made via the Admin Panel, not directly in the file system.
To narrow it down:
Editors primarily create links using page select fields (@page) in Blueprints – both in standard page blueprints and in some custom Flex-based content elements (not Flex Pages).
Images and file references are typically selected through file pickers in the Admin UI.
Occasionally, editors add links manually in the Markdown editor, especially in rich text areas, using standard [text](/some-path) syntax.
Structural elements like menus or navigation are rendered dynamically in Twig using page.find() or collections – so those links remain functional even if a page is moved or renamed.
One issue we’ve noticed specifically: When a page that was selected in a @page select field is later moved or renamed, opening the corresponding element in the Admin again shows the select box pointing to the root page instead of the originally selected one (which is now no longer resolved).
This can lead to editors unintentionally resetting or breaking the link when saving again.
Thanks again! I think your suggestion is really creative and pragmatic – especially for small to medium setups. But I’m trying to imagine how it would work in our actual setup, and I’m running into a few challenges.
In our case:
Editors frequently link between subpages, and often to specific sections or anchors on those pages (e.g. /products#features).
We use a lot of modular pages and also custom content modules (based on Flex and Blueprint forms), where editors link to other pages via @page selects or file pickers.
Sometimes links are added manually in Markdown, sometimes via fields – so there’s no single place where all links are defined.
Pages are often reorganized or renamed during content refactoring – and when that happens, a previously working link might now point to the root or become empty (especially in select boxes).
Also, editors use anchors a lot for internal navigation on long pages (#section1) – and those can break easily if the page layout changes.
So while I really like the idea of defining links in frontmatter or in a central YAML file, I fear it might be too abstract or technical for our editorial team. They’re used to the Admin UI and don’t touch Twig or raw YAML unless they absolutely have to.
What I had secretly hoped for was a plugin that could:
Detect when a page’s route changes, and
Automatically update any fields or Markdown references to that route across the site.
Or at least offer some kind of “reverse link lookup”: e.g. “this page is linked by X, Y, and Z” – so editors would know what might break before renaming.
That way we wouldn’t have to re-train everyone or change our whole content structure, and performance would remain unaffected (since most of our pages are cached anyway).
If such a plugin existed, or if anyone has built something similar, I’d be super interested.
Also, as a side note:
We’re about to start a new, large Grav-based project, and we’d really like to build it in a way that avoids these link-related issues from the beginning.
So you have best practices or architectural suggestions for how to set up internal linking, routing, or content references in a future-proof way – especially for a content-heavy site with multiple editors – we’d love to hear your thoughts.
A plugin that scans pages when created and edited each time(links may be changed) and stores links somewhere with their source would help for detecting problem. if we could make links come from somewhere and not just be text, that plugin could also change that variable easier without editing page. but i dont know much about php so i cant create that plugin for now.
btw i also try to build something like “news” website which also requires handling many writers. i did set flex for writers to enter content because i couldnt use pages because i couldnt separete permissions as i want to writers. i wonder how you handled that case. i also would like to see your website to get as example. Have a nice day, cya!