How to use Obsidian with Hugo Static Site Generator

Ignore unwanted files

All of my Obsidian notes are save in content folder. That’s mean there are notes and files that I don’t want it to be part of the site included. I have to let Hugo know to ignore them. We can set that in hugo.yaml file:

ignoreFiles:
  - content/.obsidian/\.*
  - content/10 Inbox/\.*
  - content/20 Notes/\.*
  - content/40 Projects/\.*
  - content/50 Commonplace/\.*
  - content/60 Resources/\.*
  - content/70 Archive/\.*

By default, the Obsidian’s internal links doesn’t work properly when processed with Hugo.

[[Wikilinks]] doesn’t have any relative path to notes in another folder. Make it difficult to phases the link, especially when there are notes with the same name in another folders. How would anyone know which note the link actually linked to, beside Obsidian?

It’s appeared that solution by ichmoimeyo is to change the settings in Obsidian as follow:

Under Files and Links:

  1. Set Default location for new notes -> Same folder as current file.
  2. Set New Link format -> Relative path to file.
  3. Set Use [[Wikilinks]] -> off.
  4. Set Default location for new attachments -> In subfolder under current folder.
  5. Set Subfolder name -> attachments.

This will format link to be similar to standard the Markdown links. Make it easier to know which note in which folder the link actually linked to.

Note

Even if you disable the Wikilink format, you can still autocomplete links by typing two square brackets [[ . When you select one of the suggested files, Obsidian will generates a Markdown link.

Here is the inline partial template that converts Obsidian markdown links to links that works in Hugo:

Create obsidianMd.html in layouts/partials/inline/. Copy code from below and save it to the file:

{{- define "partials/inline/obsidianMd.html" -}}
  {{- $content := .content -}}
  {{- $pageRelLink := .pageRelLink -}}

  {{- $regexLinks := `href="([^"]*)` -}}

  {{- $links := $content | findRE $regexLinks -}}

  {{- /* Run through all the links in content */ -}}
  {{- range $links -}}
    {{- $source := . -}}

    {{- /* Extract only URL from source */ -}}
    {{- $url := substr . 6 -}}

    {{- /* Skip external link */ -}}
    {{- if hasPrefix $url "https://" -}}
      {{- continue -}}
    {{- end -}}
    {{- if hasPrefix $url "http://" -}}
      {{- continue -}}
    {{- end -}}

    {{- /* Convert link to a fragment */ -}}
    {{- if hasPrefix $url "#" -}}
      {{- $baseName := path.BaseName $pageRelLink -}}
      {{- $url = path.Join $baseName $url -}}
    {{- end -}}

    {{- /* Convert URL */ -}}
    {{- $url = lower $url -}}
    {{- $url = replace $url " - " " " -}}
    {{- $url = replace $url " " "-" -}}
    {{- $url = replace $url "%20" "-" -}}
    {{- $url = replace $url ".md" "" -}}
    {{- $url = replace $url "#" "/#" -}}
    {{- if hasPrefix $url "../" -}}
      {{- $url = replace $url "../" "../../" -}}
    {{- else -}}
      {{- $url = print "../" $url -}}
    {{- end -}}
    {{- $link := print `href="` $url -}}

    {{- /* Replace link in content */ -}}
    {{- $content = replace $content $source $link -}}
  {{- end -}}

  {{- /* Output content */ -}}
  {{- $content | safeHTML -}}
{{- end -}}

Here is how to use it:

{{- partial "inline/obsidianMd" (dict "content" .Content "pageRelLink" .Page.RelPermalink) -}}

Create render-link.html in layouts/_default/_markup/. Copy code from below and save it to the file:

<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if or (strings.HasPrefix .Destination "http") (strings.HasPrefix .Destination "https") }} target="_blank"{{ end }} >{{ .Text | safeHTML }}</a>

Use this code instead of the one above. This one will also open external link in a new tab.

<a {{ if or (strings.HasPrefix .Destination "http") (strings.HasPrefix .Destination "https") }}class="external-link"{{ end }} href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if or (strings.HasPrefix .Destination "http") (strings.HasPrefix .Destination "https") }} target="_blank"{{ end }} >{{ .Text | safeHTML }}</a>

Then, in your CSS file, add the following:

.external-link::after {
  content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="rgb(136, 136, 136)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" x2="21" y1="14" y2="3"/></svg>');
  top: 1px;
  padding-left: 2px;
  position: relative;
}

This will add Lucide’s external link icon at the end of every external links.

Support Callouts

Obsidian has support for Callouts, and I want to implement that as I used it quite a lot on my notes.

Here is the code:

  {{- $content := .content -}}

  {{- $regexCallout := `<blockquote>\n<p>\[!(.+)\] ?(.+)?\n` -}}

  {{- $callouts := $content | findRE $regexCallout -}}

  {{- range $callouts -}}
    {{- $callout := findRESubmatch $regexCallout . -}}
    {{- $type := index $callout 0 1 -}}
    {{- $title := index $callout 0 2 -}}
    {{- if eq $title "" }}
      {{- $content = replaceRE $regexCallout `<blockquote class="callout $1"><div class="title">%%$1%%</div><p></p>` $content 1 -}}
      {{- $content = replaceRE $regexCallout `<blockquote class="callout $1"><div class="title">$2</div><p></p>` $content 1 -}}
    {{- end -}}
  {{- end -}}
  {{- $content = replace $content "Note" "Note" -}}
  {{- $content = replace $content "Quote" "Quote" -}}

Put it before the {{- /* Run through all the links in content */ -}} block like the following:

  {{- $content := .content -}}
  {{- $pageRelLink := .pageRelLink -}}

  {{- $regexCallout := `<blockquote>\n<p>\[!(.+)\] ?(.+)?\n` -}}
  {{- $regexLinks := `href="([^"]*)` -}}

  {{- $callouts := $content | findRE $regexCallout -}}
  {{- $links := $content | findRE $regexLinks -}}

  {{- /* Convert Callouts */ -}}
  {{- range $callouts -}}
    {{- $callout := findRESubmatch $regexCallout . -}}
    {{- $type := index $callout 0 1 -}}
    {{- $title := index $callout 0 2 -}}
    {{- if eq $title "" }}
      {{- $content = replaceRE $regexCallout `<blockquote class="callout $1"><div class="title">%%$1%%</div><p></p>` $content 1 -}}
      {{- $content = replaceRE $regexCallout `<blockquote class="callout $1"><div class="title">$2</div><p></p>` $content 1 -}}
    {{- end -}}
  {{- end -}}
  {{- $content = replace $content "Note" "Note" -}}
  {{- $content = replace $content "Quote" "Quote" -}}

  {{- /* Run through all the links in content */ -}}
  {{- range $links -}}

How to test Hugo site on the other devices within LAN

Use the following command line:

hugo server --bind <your-ip-address> --baseURL http://<your-ip-address>

To test your Hugo site with draft contents as well, use this command line:

hugo server --bind -D <your-ip-address> --baseURL http://<your-ip-address>