How to format external links at build time?

Viewed 921

I have a Hugo site with some JavaScript and Gulp.
I would like to automatically detect and format <a> links so that an "external link" icon and "target=_blank" are added.

For now I do it in the client browser with some JavaScript:

// EXTERNAL LINKS
$(document.links).filter(function() {
  return this.hostname != window.location.hostname
      && this.hostname != "domain-name-base-url.com"
      && this.hostname != "another-domain-name-base-url-to-be-treated-as-internal.com"
}).attr('target', '_blank').addClass("external");

But I would like to do it at build time for performance reasons.

How can I move this to a post-build gulp task? I tried some stuff with .pipe(replace...) but couldn't manage to do it. Can I even do it at Hugo build time?

If anyone knows even a simpler way to achieve this (allow the user to type in Markdown forgetting about whether a link is external or no, and adding `target=_blank" and an icon at build time)... any magical library or best practice?

3 Answers

Rather spend time trying to develop a solution, you could simply replace any external links manually.

With Markdown 2.5.2 you can use the following syntax

[link](url){:target="_blank"}

If that doesn't work, just use regular HTML

<a href="http://example.com/" target="_blank">Hello, world!</a>

More information about internal and external links in Hugo can be found here.

Update: You can also do this in your templates, by using the replace function to add that attribute to all anchor elements:

{{ $content := replace .Content "<a " "<a target=\"_blank\" " | safeHTML }}
{{ $content }}

You can configure Blackfriday, Hugo's markdown processor, to open links in a new tab by adding this to the bottom of your config.toml file:

[blackfriday]
  hrefTargetBlank = true

From the Blackfriday options docs:

hrefTargetBlank

default: false
Blackfriday flag: HTML_HREF_TARGET_BLANK
Purpose: true opens (external links) absolute links in a new window or tab. While the target="_blank" attribute is typically used for external links, Blackfriday does that for all absolute links (ref). One needs to make note of this if they use absolute links throughout, for internal links too (for example, by setting canonifyURLs to true or via absURL).

Hugo has all you need with Markdown render hooks: https://gohugo.io/getting-started/configuration-markup/#markdown-render-hooks

Tutorial for Hugo external link:

Create a layouts/_default/_markup/render-link.html

{{- $parsedTitle := dict -}}
{{- with .Title -}}
  {{- $parsedTitle = partial "functions/parse-title-attribute.html" . -}}
{{- end -}}
{{- $baseurl := urls.Parse .Page.Site.BaseURL -}}
{{- $desturl := urls.Parse .Destination -}}

<a href="{{ .Destination | safeURL }}"
  {{- with $parsedTitle.title }} title="{{ . }}"{{- end -}}
  {{- with $parsedTitle.attributes -}}
    {{- range $k, $v := . -}}
      {{- printf " %s=%q" $k $v | safeHTMLAttr -}}
    {{- end -}}
  {{- end -}}
  {{- if and (strings.HasPrefix .Destination "http") (not (strings.HasPrefix $desturl.Host $baseurl.Host)) }} target="_blank" rel="external nofollow noopener"{{ end -}}
  >{{ .Text | safeHTML }}</a>

{{- /* Strip trailing space. Code by djibe, thanks to jmooring & killbus */ -}}

Now you have 2 possibilities:

  • Use in markdown: [External link](https://discourse.gohugo.io/ "{rel='nofollow'}") (however all external links will be flagged external nofollow except if you remove line {{- if and (strings.HasPrefix .Destination "http") (not (strings.HasPrefix $desturl.Host $baseurl.Host)) }} target="_blank" rel="external nofollow noopener"{{ end -}})
  • or standard [External link](https://discourse.gohugo.io/)

NB. Shortcodes are not affected by this so you have to specify the suitable behaviour on every link.

Related