# Linking to a post with a URL different from its filename

**URL:** <https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538>\
**Category:** support\
**Created:** [April 27, 2025, 12:42pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538 "2025-04-27T12:42:09Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![DanielBarker](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@DanielBarker](https://discourse.gohugo.io/u/DanielBarker)\
**Post date:** [April 27, 2025, 12:42pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/1 "2025-04-27T12:42:09Z")

</div>

I am setting-up the URL config for my blog and I’ve run into a usability gotcha, which I’m wondering if there’s a way around.

When I first set up my blog I had

```toml
[permalinks]
posts = "/posts/:filename/"

```

and all of my posts were page-bundles (I believe the term is?) under one folder, so I could link to them very easily in markdown by filename

```markdown
[See here](./my_cool_article)

```

This was great because my editor (VS Code) would auto-suggest paths based on the file-system, allowing me to tab complete & be sure I had the correct path.

Now the number of articles has grown and I have split into folders per year. But I do not want the year to appear in the URL, _and_ I want to use the slug, rather than the filename. I’ve got this all set up and working fine.

This issue arises when linking, I need to remember what the slug is which, of course, VS code knows nothing about.

So I was wondering is it possible to get Hugo to render markdown links to _files_ by looking at the front matter and deriving the URL from that? (Perhaps a custom render hook would work but I don’t know enough about Hugo to do that)

Perhaps this is also an X/Y problem and I should not be linking to articles like this at all, but I couldn’t easily find any good documentation about this.

Thanks,  
Dan

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 27, 2025, 1:02pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/2 "2025-04-27T13:02:54Z")

</div>

> [@DanielBarker](#):
>
> So I was wondering is it possible to get Hugo to render markdown links to _files_

Yes. Enable Hugo’s embedded link and image render hooks in your site configuration.

```plaintext
[markup.goldmark.renderHooks.image]
enableDefault = true

[markup.goldmark.renderHooks.link]
enableDefault = true

```

Then use the page’s logical path as the link destination.

The [above won’t work](https://github.com/gohugoio/hugo/issues/13535) if your theme already has a link render hook. Let me know if that’s the case and I can provide additional instructions.

---

<div class="post-metadata">

**Author:** ![DanielBarker](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@DanielBarker](https://discourse.gohugo.io/u/DanielBarker)\
**Post date:** [April 27, 2025, 5:09pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/3 "2025-04-27T17:09:43Z")

</div>

Thanks for the help.

I do indeed have a custom render hook. It adds an icon for external links and `target="_blank"`.

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 27, 2025, 5:10pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/4 "2025-04-27T17:10:33Z")

</div>

What version are you running?

---

<div class="post-metadata">

**Author:** ![DanielBarker](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@DanielBarker](https://discourse.gohugo.io/u/DanielBarker)\
**Post date:** [April 27, 2025, 5:11pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/5 "2025-04-27T17:11:22Z")

</div>

```bash
$ hugo version
hugo v0.146.6-1e0b058efe8d6e236bc7c8d6981d9bfb1443178e+extended linux/amd64 BuildDate=2025-04-20T10:58:40Z VendorInfo=snap:0.146.6

```

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 27, 2025, 5:11pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/6 "2025-04-27T17:11:57Z")

</div>

Did you create the hook, or did it come with the theme?

---

<div class="post-metadata">

**Author:** ![DanielBarker](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@DanielBarker](https://discourse.gohugo.io/u/DanielBarker)\
**Post date:** [April 27, 2025, 5:18pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/7 "2025-04-27T17:18:13Z")

</div>

I am not using a “theme” as such, I started with a completely empty Hugo setup and have built-up everything from scratch.  
The code for the render hook I found from searching for how to achieve what I wanted wrt the external links.  
Here’s the hook

```html
<a href="{{ .Destination | safeURL }}" {{ with .Title}} title="{{ . }}" {{ end }}{{ if
    strings.HasPrefix.Destination "http" }} target="_blank" rel="noreferrer noopener" {{ end }}>
    {{ .Text | safeHTML }}
    {{if strings.HasPrefix .Destination "http" }}
    <span style="white-space: nowrap;">
        &nbsp;
        <!-- snip inline <svg> node for icon -->
    </span>
    {{ end }}
</a>

```

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 27, 2025, 5:19pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/8 "2025-04-27T17:19:54Z")

</div>

For v0.146.0 and later the directory structure is:

```plaintext
layouts/
├── _markup/
│ └── render-link.html/
├── _partials/
├── _shortcodes/
├── baseof.html
├── home.html
├── page.html
├── section.html
├── taxonomy.html
└── term.html

```

Replace the contents of `render-link.html` with [this code](https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_markup/render-link.html).

You can modify the new hook to add `target="_blank"` for external links, but I wouldn’t: most people find that behavior annoying:

[https://www.digital.ink/blog/website-links-new-tab/](https://www.digital.ink/blog/website-links-new-tab/)

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 27, 2025, 5:21pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/9 "2025-04-27T17:21:57Z")

</div>

Or just delete your hook and use the embedded hook:

```plaintext
[markup.goldmark.renderHooks.link]
enableDefault = true

```

And if you’re going to do that, and you don’t have an image render hook, then do this too:

```plaintext
[markup.goldmark.renderHooks.link]
enableDefault = true

```

---

<div class="post-metadata">

**Author:** ![DanielBarker](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@DanielBarker](https://discourse.gohugo.io/u/DanielBarker)\
**Post date:** [April 27, 2025, 5:25pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/10 "2025-04-27T17:25:02Z")

</div>

Excuse my ignorance, but what is the default hook and why do I need to enable it if not using a custom hook?  
Why do images enter in the picture, aren’t they always referred to by file path?

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 27, 2025, 5:26pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/11 "2025-04-27T17:26:58Z")

</div>

I’ve written about this many times. See this [article](https://www.veriphor.com/articles/link-and-image-render-hooks/), specifically the overview and problems sections.

Also, in my view, every new site should have this in its site configuration:

```plaintext
[markup.goldmark.renderHooks.image]
enableDefault = true

[markup.goldmark.renderHooks.link]
enableDefault = true

```

The above is automatically enabled for multilingual single-host sites.

And if theme authors provide their own hooks, they should model them after the default hooks.

---

<div class="post-metadata">

**Author:** ![DanielBarker](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@DanielBarker](https://discourse.gohugo.io/u/DanielBarker)\
**Post date:** [April 27, 2025, 5:32pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/12 "2025-04-27T17:32:25Z")

</div>

That’s a super useful write-up, thanks!  
I’ll digest it and try to update my set-up accordingly.

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 27, 2025, 5:36pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/13 "2025-04-27T17:36:38Z")

</div>

If your content structure looks something like this:

```plaintext
content/
├── posts/
│ ├── 2025/
│ │ ├── post-1/
│ │ │ └── index.md
│ │ └── post-2/
│ │ └── index.md
│ └── _index.md
└── _index.md

```

And you want to link from post-1 to post-2…

1) These make the most sense (direct match to [logical path](https://gohugo.io/quick-reference/glossary/#logical-path)):

```plaintext
[Post 2](/posts/post-2)
[Post 2](../post-2)

```

2) These also work (the `Page.GetPage` method has some additional lookup logic):

```plaintext
[Post 2](./post-2)
[Post 2](post-2)

```

---

<div class="post-metadata">

**Author:** ![DanielBarker](https://avatars.discourse-cdn.com/v4/letter/d/a3d4f5/32.png) [@DanielBarker](https://discourse.gohugo.io/u/DanielBarker)\
**Post date:** [April 28, 2025, 8:53am UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/14 "2025-04-28T08:53:16Z")

</div>

Using the render-hook code you provided I have manged to get this it working so I can link to the file path in markdown and it correctly deduces the url. Thanks!

One thing to note is I found it necessary to link to the `index.md`, and using an absolute path to `/posts/` did not work, so for example

```markdown
[Post 2](/posts/post-2) This did not work
[Post 2](/posts/post-2/index.md) Neither did this
[Post 2](../post-2) This did not work
[Post 2](../post-2/index.md) But this did

```

But actually linking to the `index.md` is better because that way my editor actually understands the link.

I could not seem to get the broken link highlighting working and searching on GitHub I see nothing in the Hugo code which would add the `broken` CSS class?

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 28, 2025, 2:48pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/15 "2025-04-28T14:48:31Z")

</div>

The results of your tests don’t make sense to me, but it’s also unclear to me which render hook you are using? Can you share your project repository?

The render hook provided in the article that I linked to is different that Hugo’s embedded render hook. The embedded link render hook does not provide link validation or highlighting of broken links. Again, having access to your project repository would be very helpful.

---

<div class="post-metadata">

**Author:** ![system](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/system/32/1_2.png) [@system](https://discourse.gohugo.io/u/system)\
**Post date:** [April 30, 2025, 2:49pm UTC](https://discourse.gohugo.io/t/linking-to-a-post-with-a-url-different-from-its-filename/54538/16 "2025-04-30T14:49:26Z")

</div>

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.
