Custom output content types

Once I finish my move of Node to Page, and everything is a Page which can contain Pages – which in itself solves some interesting problems – Hugo has a much cleaner model which can be exploited.

I see a lot of questions about rendering alternative representations of a Hugo site, or parts of a Hugo site: how can I create more RSS feeds? How to output a page or site as JSON? Create iCal calendar file? Google AMP? Facebook instant pages? How can I generate multiple representations of the same content?

For discussion, given the content tree below:

content
β”œβ”€β”€ _index.md
β”œβ”€β”€ categories
β”‚   β”œβ”€β”€ _index.md
β”‚   └── photo
β”‚       └── _index.md
β”œβ”€β”€ post
β”‚   β”œβ”€β”€ _index.md
β”‚   └── hugo-rocks.md
└── tags
    β”œβ”€β”€ _index.md
    └── hugo
        └── _index.md

Today we have some implicit default content types. RSS, html, sitemap.

If we extend on that we could define the output content types in Site config and override in page front matter, i.e.

output = ["rss", "html", "ical", "amp"]

And then we define some template lookup rules based on what gets rendered (taxonomy, home page, section listing etc.), so rendering a section for amp would look for templates like this:

return []string{"section/" + section + ".amp.html", "_default/amp.html", "amp.html", "_internal/_default/amp.html"}

Plenty of details left out in the above, but it could be plenty powerful and flexible me thinks. This also allows for creating sites without RSS, sitemap, RSS only and any combo.

Thoughts?

7 Likes

I think this is a great idea for many reasons, not the least of which is the added flexibility for pulling from content files to create new files in serialized data formats (specifically .json).

What if you skip the config piece altogether and just allow for overrides in individual front matter for specific use cases where an individual content file should not be output. This assumes the appropriate-file-extension page will be generated automatically based on the extensions alone in the layouts folder(s). Granted, this might be a bigger (breaking) change, and I think this is kind of what you’re saying already:

layouts
|_default
   β”œβ”€β”€ single.html
   β”œβ”€β”€ single.amp.html
   β”œβ”€β”€ single.json (don't konw when you'd use this, but just to extend the example)
   β”œβ”€β”€ list.html
   β”œβ”€β”€ list.json
   β”œβ”€β”€ rss.xml
   β”œβ”€β”€ sitemap.xml

##As an aside…

I’ve noticed when editing a local copy of the docs that terms are often confusing in Hugo. I would be careful with the term β€œcontent types.” This is pedantic for this feed and slightly tangential, but I think we’re talking about β€œfile type” orβ€”better considering Hugo’s use of β€œtypeβ€β€”β€œfile format” or β€œfile extension.”

This is a bit more standard, I think…

file format or file extension: .md, .html, .json
content type: post, event, author, speaker
content section or section: blog,events,authors,speakers
content model: post <==> author, event <==>speaker, etc

2 Likes

Agree, this is a great idea!

One question I have is how we go about providing a text/template renderer when requested. Outputting JSON or Javascript (or anything non-XML) with the html/template engine tends to just complicate things when the text/template engine would be much simpler. If we did offer a text/template engine, we’d need to look at our template funcs to make sure they can work in both contexts (ie. make sure we’re returning strings instead of template.HTML).

# config.toml

# Define output types used in this site
output = ["html", "json", "js", "rss", "svg"]

# Designate certain output types as text-based
outputText = ["json", "js"]

That is a very good question. I think we can make the output definition a little more complex, i.e.

output = [{name: "html"}, {name: json, output: "text"}]

In the above we default the output to HTML.

1 Like