How to use Vue.js with a Hugo project

Hi,

I have an existing Hugo project and I’m looking to integrate Vue.js to it but I’m a bit lost on how to integrate Vue.js to the project.

Should I use Gulp to be able to auto build my Hugo project and Vue.js files ?

Thank you in advance ! :slight_smile:

1 Like

For example, (for vue3)
in head.html:
<script src="https://unpkg.com/vue@next"></script>

in a file called /static/js/myapp.js

var app = Vue.createApp({
  delimiters: ["[[", "]]"],
  data() {return {
    col1cards: [{issuetype: "Feature Request", issuecolor: 'bg-green-50' },
    {description: "issuetype: "Test", issuecolor: "bg-orange-50" },
    {description: "issuetype: "Task", issuecolor: "bg-blue-50" }],
  }},
}).mount('#board');

Then, include it in the bottom of a Hugo template,
<script src="/js/myapp.js"></script>

(that would be any .html under /layouts or the themes//layouts)

Then set the ID of the outermost div of the template to the mount point specified in the Vue app.

<div id="board">
  ... the rest of the template
</div>

then your Vue app would have visibility in the DOM from there down.

For example, if that template included the fragment:

<span>[[ card.issuetype ]]</span>

Then Vue would replace the variable [[ card.issuetype ]] with the value from the app.

You might also choose to write Vue templates apart from Hugo templates, and not mix them, as in the example above, just pull them in where needed.

2 Likes

Here’s a more complete writeup of how to implement Vue in Hugo:

https://www.michaelgodeck.com/how-to/vue-in-hugo/

Hope it helps!

1 Like

I read your article and found it interesting, but I don’t understand how to implement the Vue SFC compilation.
Can You please share a complete project with a full example or more information about it?

Thanks!

I was also interested to find out how to make hugo render SFC.

I came up with this hacky solution:

{{if $vue:= resources.Get "/scripts/greetings.vue" | fingerprint}}
  {{$vueBase := (replace (path.Base $vue) ".vue" "") }}

  {{$style := index ($vue.Content | findRE `(?s)<style.*?>(.*)?</style>` ) 0 }}
  {{$style := $style | replaceRE `(?s)</?.*?style.*?>` "" }}
  {{$style := $style | resources.FromString (printf "%s%s%s" $vueBase $vue.Data.Integrity ".scss")  }}
  {{$style := $style | toCSS (dict "outputStyle" "compressed") }}
  <link rel="stylesheet" href="{{ $style.RelPermalink }}" >

  <div id="sfc">
    {{$template := index ($vue.Content | findRE `(?s)<template>(.*)?</template>` ) 0 }}
    {{$template := $template | replaceRE `(?s)</?template>` "" }}
    {{$template | safeHTML}}
  </div>

  {{$script := index ($vue.Content | findRE `(?s)<script.*?>(.*)?</script>` ) 0 }}
  {{$script := $script | replaceRE `(?s)</?.*?script.*?>` "" }}
  {{$script := $script | resources.FromString (printf "%s%s%s" $vueBase $vue.Data.Integrity ".ts")  }}
  {{$script := $script | js.Build (dict "minify" true) }}
  <script type="text/javascript" src="{{ $script.RelPermalink }}"></script>
{{end}}

greetings.vue

<script lang="ts">
import { createApp, ref } from 'vue/dist/vue.esm-bundler.js';

createApp({
  setup: () => {
    const greeting = ref('Hello World!');
    return { greeting };
  }
}).mount("#sfc");
</script>

<template>
  <p class="greeting">{{ greeting }}</p>
</template>

<style lang="scss">
.greeting {
  color: darken(cyan,20);
  font-weight: bold;
}
</style>

If somebody could make some improvements I would be more then happy :wink:
( e.g. for some reason the style doesn’t get updated when edited in the vue file while the changes in script result in an update… with the fingerprint cache trashing thats no longer the case)

I made some changes to the code, added shortcodes and also created a demo: GitHub - indus/hugoVueSFC: Hugo meets Vue's SFCs