Creating 'static' content that uses partials

We’re loving Hugo so far, but I have a use case I haven’t been able to figure out.

In addition to using Hugo to generate HTML from our markdown files that represent blog posts, I’d also like to be able to create some ‘static’ content, that is, .html files that also make use of the partial templates we’ve created and has access to the same variables that the homepage index.html does.

Our use case is that we’ve got a company site that has a main page, several additional pages about the company (like about, case studies, etc, pages), plus a blogs section.

We’re rendering all the blogs from markdown, but the about, case studies pages, etc are complex HTML on their own in which I’d like to be able to use our header/footer partials etc.

I’ve tried putting .html files with a TOML header into my content folder, but Hugo then tries to handle these the same way it does the markdown files, and, for example, my {{ partial /partialLocation }} tags don’t get processed.

Really, what I think we’d like, is to be able to place additional .html files and folders alongside layout content, and have them generated as though they were simply additional versions of the homepage.

Any help anyone can offer would be greatly appreciated.

5 Likes

I did my “about.html” and “privacy.html” pages by creating templates for them (layouts/toppage/single.html) and then creating a single content file for each, setting the type and forcing the URL to be /about.html and /privacy.html. It seemed like I was fighting the system, but it worked well enough.

1 Like

Thanks for the suggestion. It’s the kind of thing I’m hoping to avoid, because I feel the same way about it - it’s a bit of a smell to create two separate files and two separate folders to be able to have /about be my /about/index.html page.

That said, if there isn’t a way to do this cleanly, I’ll go ahead and do that to make it work (though, will pages that aren’t the top-level homepage have access to all of the variables for the rest of the postings & taxonomies as well?)

It would be nice though, if you could define a list of files in the config.yaml that you’d like processed in addition to the index.html that represents your homepage, and that they’d be treated in a similar fashion, but could have different paths/names. So, for example, I might provide this in the config.yaml:

mainPages :
“index.html” = “/index.html”
“about/index.html” = “/about/index.html”
“contact/index.html” = “/contact/index.html”

Hugo would see this and realize it needs to not only run against my /layout/index.html, but also against /layout/about/index.html and /layout/contact/index.html and where to put the resultant files.

@michael_henderson

Could you elaborate on this:

and then creating a single content file for each, setting the type and forcing the URL to be /about.html and /privacy.html.

I’m not exactly sure what it means to create a single content file e.g. where exactly would this file go? What would you set the type to be?

1 Like

I’m not quite sure what’s being asked for here, as this should be able to make this work without anything special involved, because of two things:

  1. Markdown has a rule (and blackfriday follows it) that block HTML tags in the content suspend markdown processing within the block tags.
  2. Your templates should be well designed enough that layouts/_default/single.html should be able to work so that {{ .Content }} can render your content meaningfully.

That is, it should be possible to create an about.md that contains a big <div> block containing your complex HTML. You may need to do a few things to other parts of your site to prevent post metadata from showing up, but I’ve got a template (cabaret—based on redlounge) that does this quite nicely for my website (although my about.md is markdown, I do have HTML block tags—<table>, mostly—that render correctly as well in other posts).

Caveat emptor: cabaret only works with Hugo 0.13-dev, and in fact may only work with some of my patches that may not have yet been accepted. It also is deeply modular and expresses strong opinions about that modularity, so understanding _default/single.html means understanding eleven top-level partials and an alternative .Render template that depends on twelve other partials. It’s all logical, but because 0.13 hasn’t been released and I’m still tweaking, I haven’t documented all of this.

@halostatue

It’s not HTML parsing thats the issue it’s template tags. For example something like:

{{ range first 5 (where .Site.Recent "Section" "blog") }}
  {{ .Render "li" }}
{{ end }}

does not get processed and is instead converted to a string of text.

Ah. Template tags don’t get processed in content files. You can cheat by putting what you want in a shortcode.

{{% list-recent "Section" "blog" %}}

And layouts/shortcodes/list-recent.html looks like:

{{ range first 5 where .Site.Recent (.Get 0) (.Get 1) }}
  {{ .Render "li" }}
{{ end }}
5 Likes

Hey Ryan, did you figure this out? I still can’t get my head around how to actually do this.

@brad, sorry for the delay.

I have three files that work together on this:

config.toml
content/top/about.md
layouts/top/single.html

The “single content file” that I mentioned is the layouts/top/single.html file. The “type” of content/top/about.md is “top”, so Hugo looks for the template in layouts/top/single.html. The rule is that /content/SSSS/zzzz.md will be rendered with layouts/SSSS/single.html if it is available.

In my config.toml file, I have

[permalinks]
	post = "/blog/:year/:month/:day/:title/"
	top = "/:filename/"

Since the [permalinks] in my config says top = "/:filename/", Hugo will render about.md to public/about/index.html. The :filename seems to mean “the basename of the file,” so it strips the path and the “.md” extension.

When I first started this, I also put type = "top" in my about.md file. It was simpler to just create the section content/top and use that. That way, nothing special needs to be in the markdown file.

1 Like

Thanks! It took me some experimenting but i did figure it out. I have a blog archetype and i was trying to create a blog archive page. Basically it looks like a heading for each year followed by an unordered-list of the blog post titles. Here’s what i did:

/content/archives/yearly.html

---
title: "Blog archives"
url: "/blog/archives/"
type: archive
---

/layouts/archive/single.html

{{ range (where .Site.Pages "Section" "blog").GroupByDate "2006" }}
  <h2>{{ .Key }}</h2>
  <ul>
    {{ range .Pages }}
      <li>
        <a href="{{ .Permalink }}">{{ .Title }}</a>
        <span>{{ .Date.Format "Jan 2" }}</span>
      </li>
    {{ end }}
  </ul>
{{ end }}
1 Like

I was facing this problem too, used @michael_henderson’s original idea to fix it with type: page in the about.md and then creating a layouts/page/single.html.

I think it’s cleaner because a page at root can stay at its right place in content and there is no extra index.xml generated for top type which is an unintentional side effect in Mike’s current approach.

If others agree on this approach, I will be happy to add it to Hugo docs.

4 Likes

Thank you for Hugo!
I am having a lot of trouble with this one. I will be using markdown for blog posts soon but for now am laying out the structure and design parts of my site and that relies heavily on HTML. When I put extensive and precise HTML into markdown files there are always little rendering issues so that approach is out. I’d really like to code straight HTML and be able to pull in all partial templates - just like I do on the homepage (index.html) - but for any page I need to.

The only way I can see to do this is by forcing the type and URL technique that’s been discussed, but that seems like a bit of a hack and seems messy. Is that the official way that Hugo wants us to do it or is there another solution?

Thank you again

That’s a fine way to do it given your constraints.

Steve Franciaspf13.com@spf13

I accomplish this by adding a ‘page’ parameter in the content’s front matter. Then whenever I render a single page I do a check if the page parameter is set, if so then I render it as a partial.

Within a piece of content’s front matter:

+++
page = "page/custom_page.html"
+++

Within the single.html page (belonging to the section you want to render):

 {{ if .Params.page }}
        {{ partial .Params.page . }}
 {{ else }}
 <!-- Render content normally -->
 {{ end }}

(Note, I don’t use with .Params.page because then the partial doesn’t work.(due to passing the environment through $.??)

This template will render the parial in layouts/partials/page/custom_page.html.
This partial can contain other partials or template code.

Anyways, I added this ability to render things that don’t really fit in a section or taxonomy(like “archive.md”, or content that can’t be rendered in a general way like Project. Project could be done generally but would be too complex with screenshots, features, feature thumbnails, links, etc. (I don’t think Hugo supports user-defined TOML tables? I may be wrong, haven’t tried it within the front matter yet.)

I’ll be announcing a project I’ve been working on soon, once I get some better documentation and tutorials setup around it.

3 Likes

Thanks for that suggestion.

I’m wondering whether this is a concern to others - basically, this still means we are creating what is essentially content and storing it under layouts - when it seems like as our websites grow it will be more elegant to store content in the content folder.

It is something that we need to find a "best practice " for and then decide if it is solved by code or convention

Here is a way I’ve found to address this issue. It relies on existing Hugo functionality, albeit some undocumented things.

1. Organize content in the content folder
Organize the content in your content folder the way you want it to be organized in your site. No need to override with URL in frontmatter. They key is remembering that a single file will turn into a folder as follows:

Content Folder:
photos.md
writing.md
writing/comedy.md
writing/history.md
writing/history/romanempire.md
writing/history/romanempire/vol1.md
family.md

Will produce this:
http://domain/photos/
http://domain/writing/
http://domain/writing/comedy/
http://domain/writing/history/
http://domain/writing/history/romanempire/
http://domain/writing/history/romanempire/vol1/
http://domain/family/

2. You can put complex HTML into the content pages, or just use Markdown, or a combination
Below the front matter (which I’ll get to in a second), you can put HTML if you want. There are still some issues with how a mardown file renders HTML. Comments sometimes get translated literally and sometimes a P tag is wrapped around the HTML when it shouldn’t be. I think these are known issues being addressed by Black Friday? But there are workarounds to this.

The easiest workaround if you want to use complex HTML is to simply rename the content file with an .html extension. This isn’t documented, but it works great. For these files, HUGO treats them exactly the same except Hugo won’t try to change any of the html inside.

3. Setup and use layouts to construct your final html pages
I had been stumped how to construct list layouts for sub-sections since Hugo by default only recognizes a top folder as a section. For example, how would you construct a layout summarizing all of the articles under writing/history? However, the easy answer seems to be to use frontmatter.
a) First, assign each content page a type. Start broad, because Hugo’s excellent abstraction abilities let you save being specific and narrow for when you need to be which is later. So start with setting type = “page” in content/writing/history.md (or content/writing/history.html). Then assign the content a layout, like layout = “page1”.
b) Then create a folder layouts/page and a layout at layouts/page/page1.html
c) The layout can look like this:

{{ partial “header1.html” . }}

{{ partial “headerNAV.html” . }} - in the header, you can use things like {{ .Title }} and {{ .Params.___ }} to pull out things specific to the piece of content that were set in the front matter.

{{ Here is where you put your Go and HTML code to list the pieces of content you want to list, likely filtering by a custom parameter you set in the front matter and access with .Params.parameter. For example, you can list just pieces of content having to do with history.}}

{{ partial “footer.html” . }}

OR Say you don’t need to list pieces of content. That is, on the http://domain/writing/history/ page, you just want to write something and show images, all marked up in a lot of HTML and javascript. All of that HTML stays in the content file as per above (at content/writing/history.html for example). The Javascript goes in the footer partial. And the rendering layout (layouts/page/page1.html) just looks like this:

{{ partial “header1.html” . }}
{{ partial “headerNAV.html” . }}
{{ .Content }}
{{ partial “footer.html” . }}

Remember: Keep things simple by using custom front matter parameters and accessing them in partials with .Params.____ so you don’t have to use a lot of layouts. Then when you need, create a new layout and set it in the front matter with layout = “newlayout”. Then when you have a lot of layouts, create a new type and a new type folder. Scale up in this order and you can have a complex site without too many layouts.

4. Addressing relative links
Most people here are probably much more experienced than I at HTML coding, but as I built out a site with many pages and subsections I stumbled on setting up links correctly. In case it’s needed, I found it easier to set the basehref in the frontmatter of a bunch of files and that way I only need a single header1.html partial that includes the following line:
<base href={{ .Params.basehref }}>
Just passing it along as another useful way to use Hugo’s variables and front matter.

19 Likes

+1

A best practice needs to be identified and included in the docs.

Having just tried this myself the simplest solution seems to be to put your markdown/html file in the root of the content directory, and to create a layout in layouts/_default/single.html. This did exactly what I would expect, which is to create a directory in /public with the name of my markdown file containing an index.html file.

I suppose you could add something to the front matter so you could filter these ‘static’ pages out when listing content from other sections.

If you need to get fancier in terms of layout on a page-by-page basis you can still do that.

3 Likes

What I expect was place a html in layout directory like home page.

.
..
layout/
  index.html
  about.html

So I gave it a try, but found Hugo didn’t render it.

No, and this is as documented. See Getting started | Hugo

1 Like