How do I add css/js to `<head>` from partial content/template

I have some foo.md which is using some foo.html template.

Say that I add css: /static/css/foo.css in foo.md. What would I need to do inside foo.html to insert a <link href="/static/css/foo.css" media="all" rel="stylesheet" type="text/css"> inside <head> which is defined in baseof.html ?

I want to avoid creating spurious <script> or <style> inside <body> and want to place them properly in <head>.

You define a block for your head element in baseof.html, and override it in foo.html.

The example at https://gohugo.io/templates/base/#define-the-base-template shows multiple blocks defined.

Yes I already thought of block but (unless I misunderstood the usage) then the entirety of my template foo.html will be replaced with the block if i say define "mycss" at the startā€¦ I wanted foo.html to do other things as well.

Specifically, say I have {{ block "mycss" }} inside <head> inside baseof.html. Then I define css: /static/css/foo.css inside foo.md. If I have {{ define "mycss" }} at the start of ā€œfoo.htmlā€ then all of foo.html will go inside <head> ā€¦ and my parameter ā€œ.Params.cssā€ isnā€™t used. Is there now function that I can call inside foo.html that I can push some rendered html inside <head>?

Must I define a second template customcss.html with {{ define "mycss" }}{{ <link rel="stylesheet" src="{{ .css }}" /> }} and then add inside foo.html a line like {{ partial "customcss.html" (dict "css" .Params.css) }} ?

EDIT: Also, what if I want to add several <link> from various partials? do I need to define several blocks in baseof.html with different names or will a single {{ block "mycss" }} concatenate whatever number of partials I call which have {{ define "mycss" }} ?

I guess I can add a {{ partial "customcss.html" . }} inside baseof.html and define some array of css files in my foo.md which is pulled and rendered by customcss.html. Are there better ways?

This is actually an interesting question since it applies to many situations. For example, if I want to include the css for font awesome but only if it is used on that page.

I tried:
layouts/_default/baseof.html

<head>{{ block "awesome" . }}{{ end }}</head>
<body>...</body>
{{ define "awesome" }}{{ end }}

layouts/shortcode/glyphicon.html

<span class="far fa-{{ .Get 0 }}"></span>
{{ define "awesome" }}<link rel="stylesheet" href="./css/fontawesome.css" />{{ end }}

But all pages get the awesome link in their head, regardless of whether the glyphicon shortcode was actually used. The mere presence of layouts/shortcode/glyphicon.html is sufficient.

So what would be the way to obtain this?

1 Like

As I was saying above, I solved it by using a partial instead of a block, because i am of the opinion that such definitions (css files to include) should be part of the content file (.md) and not of the template.

In baseof.html add {{ partial "customcss.html" . }} then make customcss.html have the following content

{{ range .css }}
  <link rel='stylesheet' href='{{ . | absURL }}'>
{{ end }}

then put the following in your content .md file (yaml in my case)

layout: some_layout
css:
  - css/mylocalcss.css
  - https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
...

Then you donā€™t need to worry about shortcodes or template files and can keep your template files clean. If anyone has a better solution then please post.

Nice.
However, I consider it a disadvantage that Iā€™d have to specify in the content page what css it is supposed to use. In particular if the css is not actually required by the content page directly, but by deeper level templates/shortcodes/partials.

You donā€™t have to, itā€™s a solution I built for myself. Page level css dependencies should be in the md. If you want site-wide css then you can put it in baseof.html. If you want css that is template/layout level then using a block in baseof.html and a define in the layout.html should work (shortcodes are for usage inside .md as far as I understand it ā€¦ I just started using Hugo and Iā€™m not terribly in love with the Golang templating ā€œlanguageā€).

A dev or more experienced users please correct me if Iā€™m wrong or please suggest better ways of doing it. Surely this must be a common enough design issue that has been solved before.

This is interesting - Iā€™m trying to do something similar with a shortcode to add custom Google Fonts

The goal is avoiding the user having to modify their theme - and ideally have a hook into the to add the <link rel=

@davidsneighbour suggested having 2 shortcodes - one for the stylesheets and one for when the user wants the font / glyphicon. (though i may have understood this suggestion)

The problem is (remains) that a particular shortcode or partial must be able to set some kind of flag that can be tested in the <head> to pull in css/js/fonts/etcā€¦

I already posted a suggestion to address that here. Have you tried it?

Emphasize the word ā€˜shouldā€™.

See my posting #4:

Well, you used a shortcode, which is different than what I suggested.

That makes no difference. So, again:
layouts/_default/baseof.html

<html>
<head>
<title>Minimal</title>
{{- block "customcss" . }}{{ end }}
</head>
<body>
{{- block "main" . }}{{- end }}
</body>
{{- define "customcss" }}{{ end -}}

When hugo generates pages, the <head> only contains the title.

Now create layouts/partials/nifty.html:

{{- define "customcss" }}
<link rel="stylesheet" href="css/custom.css">
{{ end -}}
<!-- and some html -->

The idea is that when this partial is used on a page, the custom css will be added to the <head>. However, the mere fact that this partial exists causes all pages to contain the custom.css link.

Minimal example to play with: https://www.squirrel.nl/pub/xfer/uploads/3CiMEx-kh2w2TWxNQg98x1iQ.zip

Ah, yes. I too mentioned an issue with block in my 2nd post. I understand that you want the layout to dictate the css but push it to . Iā€™d be interested in a way to do that too. For now my solution was to trigger it from the .md, which creates some duplication if you have more than one .md using the same template.

It seems block/define is the equivalent of compilation time parsing? They get populated regardless where you put the define? But that canā€™t be the case as there are multiple files defining ā€œcontentā€ for example.

EDIT: Hold on, why do you have {{- define "customcss" }}{{ end -}} also at the end of baseof.html? You shouldnā€™t have any defines in baseof.html.

Your MWE isnā€™t producing multiple pages. Doesnā€™t seem like an actual MWE. Youā€™re not calling the nifty.html partial from anywhere

I was hoping these forums would provide more help by now but perhaps itā€™s worth asking a question on one of the stackexchange forums.

The define in baseof.html is to supply a default. I seem to recall in one of my experiments hugo complained about a missing definition for the block. Apparently I was mistaken, so the last line of baseof.html can go.

Not yet. Even without being called the custom css is already inserted in all pages. Calling it will not change that.

If i call the partial then that page ends up empty. Every other generated page seem to get the customcss. if you comment out the partial, then that page gets generated, with the custom css.

However, both single.html and list.html layout file do define "main" so I canā€™t say I understand how block actually works any more ā€¦ is this a bug?

MWE: https://send.firefox.com/download/f93d9662893fc89a/#AacIlNfi1PFuLAcYSwd4Cg

In single.html you have added code outside of the definition of ā€œmainā€. This is documented to generate unexpected results.

check baseof.html for a head partial, my looks like

<!DOCTYPE html>
<html {{ with site.Language.Lang }}lang="{{ . }}"{{ end }} >
{{ $section := .Section | default "home" }}
{{ partial "meta" . }}
<body>
{{ partialCached "aside" . $section }}
{{ partialCached "navigation" . $section}}
{{ block "main" . }}{{ end }}
{{ partial "footer" . }}
</body>
</html>

Your partial could be named head etc ā€¦

check this partial out under layouts/partials

something like (reduced!!)

<head>
<meta charset=utf-8 />
<meta name=viewport content="width=device-width, initial-scale=1" />
<base href={{.Site.BaseURL }} />
<title>{{ site.Title  | markdownify }} &ndash; {{ .Title  | markdownify }}</title>
{{ partialCached "favicon" . }}
{{ partialCached "css/css" . }}
{{ partialCached "js/jquery" . }}
{{ if eq .Params.Layout "gallery" }}
	{{ partialCached "css/fancybox" . }}
	{{ partialCached "js/fancybox"  . }}
{{ end }}
</head>

I used PartialCached to include all CSS files, JQUERY and fancybox, if Iā€™m using the gallery layout.

@ju52 Thanks for the feedback. Your setup works but the inclusion of the css files is controlled by page frontmatter. What I would like to achieve is that the inclusion depends on whether a particular feature is actually used on the page.