Skip to content

Markdown Links

There are a couple of straightforward ways to modify your Hugo single.html template to ensure that all markdown links open in a new tab: by using a JavaScript solution or by using Hugo's built-in Markdown Render Hooks.


The easiest and most common approach is to use a small block of JavaScript in your single.html (or in a partial loaded by it). This script targets all <a> tags within the main content and adds the target="_blank" attribute to them, typically only to external links for better user experience.

Steps for JavaScript

  1. Edit layouts/_default/single.html: Open your main single-page template.
  2. Insert JavaScript: Add the following script, ideally just before the closing </body> tag, or after the main content is rendered.
<script>
  // Get the main content container (adjust selector if needed, e.g., #content, .article-body)
  const content = document.querySelector('.article-content');

  if (content) {
    // Select all <a> tags within the content
    const links = content.querySelectorAll('a');

    links.forEach(link => {
      // **Recommended Check:** Only open external links in a new tab
      // This checks if the link's hostname is different from the current page's hostname.
      if (link.hostname !== window.location.hostname) {
        link.setAttribute('target', '_blank');
        link.setAttribute('rel', 'noopener noreferrer'); // Best practice for security
      }

      // **Alternative (for ALL links):**
      /*
      link.setAttribute('target', '_blank');
      link.setAttribute('rel', 'noopener noreferrer');
      */
    });
  }
</script>
  • ⚠️ Crucial: You must replace .article-content with the CSS class or ID that wraps the rendered Markdown content in your specific theme. Common values are main, article-body, or entry-content.
  • The line link.setAttribute('rel', 'noopener noreferrer'); is important for security and performance when using target="_blank".

2. Hugo Markdown Render Hook (Advanced/The Hugo Way)

For a more robust, server-side (no JavaScript required) solution that specifically targets Markdown links, use a Markdown Render Hook. This method ensures only links created via Markdown are affected.

Steps for Render Hook

  1. Create the Hook File: In your Hugo project structure, create the directory and file: layouts/_default/_markup/render-link.html

  2. Add the Template Code: Add the following Go Template code to this file:

<a href="{{ .Destination | safeURL }}"
   {{ with .Title }} title="{{ . }}"{{ end }}
   {{ if strings.HasPrefix .Destination "http" }}
     target="_blank" rel="noopener noreferrer"
   {{ end }}
>
{{ .Text | safeHTML }}</a>

How this works:

  • This hook overrides how Hugo renders a link created in Markdown (e.g., [Link Text](url)).
  • strings.HasPrefix .Destination "http": This is a robust check that only applies target="_blank" to external links (those starting with http or https).
  • If you want ALL Markdown links (internal and external) to open in a new tab, you would simplify the hook like this:
<a href="{{ .Destination | safeURL }}"
   {{ with .Title }} title="{{ . }}"{{ end }}
   target="_blank" rel="noopener noreferrer"
>
{{ .Text | safeHTML }}</a>

While Hugo's Markdown Render Hooks (like render-link.html) are specifically designed to override the rendering of Markdown-formatted links (e.g., [text](url)), they do not automatically apply to plain URL text (e.g., https://example.com) that Goldmark autolinks.

To apply custom attributes (like target="_blank") to both explicit Markdown links and autolinked plaintext URLs, you need to use two different techniques in combination.


This is the method you've already started. It handles links created like [Hugo](https://gohugo.io).

  1. Create the Hook File: layouts/_default/_markup/render-link.html

  2. Add the Template Code: Use this code to ensure Markdown links open in a new tab:

    <a href="{{ .Destination | safeURL }}"
       {{ with .Title }} title="{{ . }}"{{ end }}
       target="_blank" rel="noopener noreferrer">
    {{ .Text | safeHTML }}</a>
    

    If you only want external Markdown links to open in a new tab, use the conditional logic:

    <a href="{{ .Destination | safeURL }}"
       {{ with .Title }} title="{{ . }}"{{ end }}
       {{ if strings.HasPrefix .Destination "http" }}
         target="_blank" rel="noopener noreferrer"
       {{ end }}
    >
    {{ .Text | safeHTML }}</a>
    

Plaintext URLs are handled by Goldmark's Autolink extension. To apply custom attributes to these, you must use a feature in the Goldmark configuration called Attributes.

  1. Enable Goldmark Autolink Attributes:

    Edit your Hugo configuration file (e.g., config.toml or config.yaml) and add or modify the following block to enable attributes for the autolink extension:

    In config.toml:

    [markup.goldmark.extensions.attribute]
      autolink = true
    

    In config.yaml:

    markup:
      goldmark:
        extensions:
          attribute:
            autolink: true
    
  2. Use Markdown Attributes in the Content:

    Once enabled, you can define a special attribute string that Goldmark will automatically append to all autolinked plaintext URLs in your content.

    Since you want the attribute to apply globally, the easiest way to inject it is to define a custom template or override the content rendering process (though this is more complex).

The easiest way to apply attributes to autolinks is typically through the same JavaScript solution that was recommended previously, as Goldmark lacks a built-in global way to inject attributes into all autolinks without overriding the renderer itself.

Here is the combined, most reliable server-side + client-side approach:

Element Method Implementation
Explicit Markdown Link ([text](url)) Render Hook layouts/_default/_markup/render-link.html (Server-side)
Plaintext Autolink (https://url.com) JavaScript Script in single.html to target all links (Client-side)

By keeping the Render Hook for explicit links and using the small JavaScript block for the autolinks, you ensure a complete solution with minimal complexity:

<script>
  // Target all links within the main content
  const links = document.querySelectorAll('.post-content a'); 
  // (Adjust .post-content to your main wrapper class/ID)

  links.forEach(link => {
    // Check if the link has been processed by the render hook (optional check, but good practice)
    // If you don't use the conditional logic in the render hook, you can skip this check.
    if (!link.hasAttribute('target')) { 
      link.setAttribute('target', '_blank');
      link.setAttribute('rel', 'noopener noreferrer');
    }
  });
</script>