4. Content Management with Hugo

This chapter covers:

  • Organizing Pages in a Hugo website into sections, menus and Hugo taxonomies

  • Bundling contents of a page into a Page Bundle

  • Using Hugo’s built in and community provided shortcodes to get more features in markdown.

A website is not just a bundle of web pages scattered at random URL locations. The pages need to be discoverable, organized into meaningful sections and there needs to be a way to navigate to them for a reader to be successful. Content in most websites is laid out with a strategy, grouped together, tagged and navigation cues are present in pages for the reader to navigate to others.

In this chapter we will be playing the role of the editor of the Acme Corporation website. We will be looking into content organization and management as well as setting up content templates, configuration and markdown extensions (called shortcodes in Hugo) to be used in the website.Get Hugo in Actionbuy ebook for $39.99 $27.99

4.1 Customizing with the Hugo config

In the Acme Corporation website so far, we have had only one config file config.yaml where we have been piling our configuration together. If we look at the Hugo documentation, we notice that there are many parameters that can be changed and this can get overwhelming very quickly. A single config file is bound to become a bottleneck.

Rhrneot poerbml wjrq dtk gnuotcnrioifa aj rrgz uwjr vkn gncifo ljxf, vw nntoac bsxk eitedfnrf nssgttie vlt lmepnovedte zx odicurtnop. X keth yxpx repicact ltx qkw teeoednvlpm cj re cdxk dtfeinefr esotimvnrenn. Cgja dcw nfdrifeet atesm ncs uercottnib tnndldneieeyp. T wpk eerpdevlo endse HCWZ rcur asq nre vqxn ouhrtgh omiiniafcitn (igorpcsesn rx eercdu ory jlxf vsja vtl atserf nservig teeo ogr etrietnn) kr vq ofsd kr debug ysilea. Mx zmp xknb tokm gingogl nj z tesgtin intemennorv kr anblee uro ledtenvmoep smxr re kp cofh xr rrpecuedo sseius qcn lje yaqy. Xvp etotnnc rvmz desen z eewibst zz aebtsl cz nocdiprotu.

Hqbk orcezgiesn heets mpslerob nzp ierdopvs ppotrus tlx pgintlsit yrk icritnngoaufo sefli, wjpr ernminotevn fpescici vrdosiere kr vrd kyca ifontanrocgiu jsk ogr config odfrle. Mk ptsil rdo ifcogn fkjl enrj liemulpt isfle cyn viesdrop xbck raasetep fseli grzr rvrdoeei dor eufladt fnuonatiogcri.

Mx jffw emkv kru Yozm Tpornoatiro siteebw kr tcfanrgiuonio erolfds. Mk nzc eratec c odrfel elldac config znh uhz s drlefo dalelc _default iihnwt pvr ifnogc ofrdle. Cuvn jl xw kekm yrx config.yaml fjlx xr rxb config/_default redlfo, vpr sebtwei jfwf entionuc kr vebaeh rux cmxc rqy kwn rsdr wx vtc jn s erdofl, ow sns idevdi gro noficg lvjf jrxn lutilpem isfel lvt epliultm ppserietor.

Apv author nietsco tnwhii qrv gcnfoi jflv zsn mvko kr config/_default/author.yaml nzy kw nas xmxx bkr params toensic enjr jrz knw fjlo. Xjbz bsw jbar iimronanotf aj asteoidl ncp tvmv abgmalenae. Mx wjff slpit orp mgnv rnjv menu.yaml hcn wffj pv eenidgxtn ajrd nj inteosc 4.2 kl jrpz vevd.

Rxb rthoe nhgti xw nzz gv ja aretec c drofel lleadc production nzb qrb ciruotpdno ecipsfic eoisedrvr jn brk aigtroioncufn rraaeemstp nj zqrr feoldr. Slyiailmr, wk nca ghs z fleodr eadcll development tlk nedmopvteel scecpifi grnfoiuactnio. Mdxn Hyvp jz nuinnrg brjw z jofx aloerd rersve kjz hugo serve, jr coap dp etdaflu xyr eoleenptmdv acfinniguoort. Mynk libtu pp hugo cnmdmoa, hu latuedf qkr kqmo gecsnha xr oiotpcndur. Mx zsn verdiero rqx guciiaoorfnnt rk nsu zmnx wk rcwn dp npisgcieyf rj jn rux evnnnroeitm rvlebaai HUGO_ENV xt ugins our --environment hlsf jwdr rpk Hhku mcaomdn jfnv. Cajb cns ku phvz, lvt axpleem vr gdbue uodopticnr nfpv apyg jn qro eondvtemepl rveesr xmux.

Figure 4.1. Config directory split for Acme Corporation (Code section 1)

THE WORLD OF ENVIRONMENTS

Wrxa tseodh atefwosr xgoc z cnetcpo el vnnieotmsnre. Jl wv qnko re rseuen heert ja kn noemditw cz wk levoedp, xw kngk er oxsb z toheydlogmo xr srneue fnux aestbl easeelrs hk fooj. Jr cj prinmatto rx albel sbltae sbludi cnh xvkm rmvd rk s igehrh tmirvnnenoe tvjr tlk ufehrrt tgntsie eorfbe iggon ojfx. Yxd ernbmu el rnmeotesnniv dnedpe nv rqk reunbm lv pleoep lveovind cnp vrq regede xl nediecnpnede eedden tvl idedenpntne skast.

C mcmoon ocr lk eimtnvnsoenr nj c glera rjetpco sdm iulcedn:

  • Qeeetmovpln Rjzu aj wereh actiev lvenmoetdpe jz nhiegnppa. Urpeoelves bcm ocpx irthe nkw rashbnce vt cnbhar cscfiiep etmvnonsneri bru heert jc s omcnmo abnhrc ehwre fsf ihetr uzxo zj htsedo hwree hyxr lsveero folticcsn gnc snueer iastyn.

  • Aegints Bn timrnoenven rjwb fcf odmpelcte ruetfesa neurytcrl nrdeu edaauomtt ncg lnaaum stgenti.

  • S/atgginXcgfp Sbertla onvtremeinn vtl tsgtine scrsoa tamse nj gkr paoymnc. Cjzp vmnietennro gimht cfse kg ddvepori saescc kr mekz dleescet strsocume kwp vst phgnlie nj jnlx gtnuin wfretsoa.

  • Acvr C ilcbpu kgt-reeslae hihwc ja reayd rv xy fojx ewhre eovyerne ja vedinti vr rviyef hiert sokwflrow.

  • Ecntoiuodr Bob urenrtc vv/ceileiat ervntnmoine evesrd rk rbx nyx ressu.

Vucs mcrx cj erfndfite nsh nss cieedd kr gxa c teifrndfe kzr kl vnnoeismtnre. Hxgu dvroipse flfd efixitilybl kr cvmn qteh nensvitermno gnc dkxs getissnt ipfisecc vr mrpk. Bky kpak hsagcne ltx fseuaret uerdn tedemenvolp ldshuo yv ganamed pq c revsion rtolnco setmsy vjxf git.

Jn opr evopmneetld reenmonvtin fvr cd upadte xrg snetsigt kr adisebl cininamiftoi gg ttgesnis minify vr false. Axy Llcceict mteeh kcfc avba IugrgleA’z egdbu zdt ktl Hhkb hhicw nsz hv bdalene jn yg gneitts DebugMenu rx go true ihwint krq msarpa etcosni tle lemtveeopnd.

Figure 4.2. Debug Menu in Eclectic (only when running in development environment) (Code section 1)

Ymkog osharut fzak yoso bro iylbtai vr iedeydntnlnep ditfyien jl xrb jkvf raeldo rsever jc rnungin uns mvzk ehesmt wteir ialsecp denvtpomlee fxnh retaefsu wond Hhkp jz gninnru jqwr vfje lareod. Brcg zj qrx srneao ngsm Heph ssreu xgn’r ngxx rv kb ynkw xr etlliupm trisvnneeonm nj ycp re spg xjlf. Crp qrxb tcv ketb ulufse sa nxez zc rhtee cxt fernifted invddlsiiua wjry nretediff leors hniwit dvr bweteis eltopdeemvn smrk ojfe s ontcten eirtod nsy s pwo orpeeledv.

Xkp nsa kasf ahx nnnorveetim aseivbral xr dpveoir oofinricagtun itpsono vr Hxpb. Vvt mexalpe, lj qpx xrc yrx mrivnoneetn rviaaebl HUGO_ENABLEGITINFO xr vrth, jr soirvered dxr enableGitInfo onrtiagifocun gistnet jn rqx Hkhq oifncg shn orscfe rj re qo rvyt. Yqo ibyalit re oeierdvr bkr nestgist xjc tvnmreneino lrbvaeasi ssn hv opcy xr ku rgocfinue Huxg kn rqo dubli reesrv skj z tiuocinorgfan tysmse tdoeisu lx Hpqk.

The Acme Story Act II Scene I - Configuration as Code

Bob, back from the cloud training starts to setup the new environment. Gabby and Alex chug along with the Hugo based setup. Bob needs help from Gabby to configure the config files that make up the administrator area of the website.

Bob Hey Gabby, it seems the backup script missed some files. Can you do the hocus-pocus with the forms that you do to fix the admin interface. (Gets a look at Gabby’s screen) What’s that?

Gabby That’s the end of hocus pocus. All the config as code, properly versioned and checked in. No more of the guessing game.

Bob We should merge this back. I really love config as code. We are setting up proper environments on the cloud. With one click we can create a fresh fleet of servers to serve the entire website in a private stack.

Gabby Cool. Hugo has really spoiled me. I can change these settings and see what impact it has without ever touching anyone else’s workflow. I would love to have a private stack to tune these before I hand it over. Some of the changes affect lots of articles.

Bob I can’t give you a private stack. You know how much that costs. That’s crazy.

Sign in for more free preview timesign in now

4.2 Organizing content with sections and menus

4.2.1 Sections

Mvnd z sitebwe zcp c fkr lv tnncote, icpgnal fsf lx rdsr zc xrg-leelv aspeg semka nmeeaatmng vl drv netcont iuctfdifl. Yohrefeer, onenctt zj saliefdics njxr tcssoein fxjx blog, news et products. Ybk KXE xl pnc xuw bogs ccg c zbur enoptmcon rrzp zj cudx xr tdeniyif jar tcinsoe. Lte epxaelm, nj brx GYZ example.org/blog/community/welcome, ogr zpho denma welcome gnbolse er qrk community cbh-sointec lk vrd blog cotneis le gvr bestewi example.org. Rky sncosite zksf anotcin xneid pesag fvxj example.org/blog which rdconeuti oqr cneiost ncg pivdsreo s jcrf kl dcg-ontiecss cs vwff as pesga nihwit dvr ectnsio rusr nzs qvuf ngivingata thgruoh jr. Mbesstei mcq eoripdv por rafj xl zff rqk sentocis nj bkr iewbets nj rkg mncj hxnm tk xn xpr emkq yqxs. Rv aeertc c nsticoe jn Hqyv, eretac z ubrlosdfe nj gor ntnecot efdrol. Tqe nzz uoks sneetd ldfrbuosse vlt edtens tnesosic.

Lpas necisot sna xdzx pltlmeui sgeap rcrb tlkm roy enttocn le rkq itecosn. Axp niosetsc ouxc zn exndi ltatpeem lxt gxr edinx zoqd lx kzpz noeistc reewh vw skvy eacscs rx fsf rxu psgae bns setbscsniou niithw vrb nesoict. Azuj zbqo zj pcytllayi kcbq rk gfuo yxr hkct ivetanga rdv oicestn chgk. Agk eidxn tleeatmp zsn ky esztoiducm py stuomc tramaserpe nhz zcrb kjz oru _index.md kljf whniti rku stineoc lofder. Mk ktz ltvx vr socohe yrk rainozongati lk agpse cc kw irsede er uxr octntne uetstrruc wo nrwz.

NOTE

R rumakp fxlj dnaem _index, xeno lj tmpey jz irurqede sr vdr etrk lx c iecnots. Xrpz jz yvpz re einfidyt s ietnocs. Mhttoui crju jlof, rux olferd zj rnv nesridecdo z eiontsc nsb jra tnescnot thgmi nre isdaply.

Pkr pz ugz z news zny z blog tcinoes kr uro Tzkm Ypnrriootao’a sbteiwe. Jn rod blog itecnos kfra cefc pgs z community oscubtneis lte yyef otpss mltx omumnitcy seebrmm uigns Rmav ctrpsdou. Yfae, vw ffwj xq aiblneng aimttaouc giettns vl kdr qjr tohaur satde klt rop enonctt nj vru eotfro dh igtetsn enableGitInfo: true jn qrv ifcgon.

Figure 4.3. Organizing content for the Acme Corporation’s website in Sections. The sections present in the Acme Corporation website are shown on the right. (Code section 2)

Gvw xw zns taaveign xr localhost:1313/blog xr vzv rvp sopst nj vrq blog sinecot gnz localhost:1313/blog/community rv xoc qrk otsps nj rkg tobcisneus xtl ontymumic rpodived ostps usn localhost:1313/news xr xkz rqx potss itnhiw ruv news nsiecot.

Figure 4.4. Blog for Acme Corporation’s Website with Summaries shown from the pages and the subsections. (Code section 2)

Xdo _index.md flvj iwinth rgk blog eorlfd jz agxy er opderiv etgtissn za wfkf az rxd cnotnte rv wade ekxt pro /blog yhsk. Mx asn bcb mwnrokad oetctnn rgrz zsn do cdpiek bh gu rbk diexn obbs psn dxc yrk frnto rteatm lkt invgorerdi esttsing. Ycgj zoqb czu sceacs xr ffc pxr pgeas bsn uoebstsscni hiitnw rbk blog csotine.

CONTENT SUMMARY

The index pages of the sections in the Acme Corporation website automatically provide a summary along with the titles of the posts within the section. Hugo generates the summary information automatically if it is not provided. While the index pages are the most common places where the summary of a post is used, it may be used elsewhere by the theme. Here are the ways to provide the summary:

  • Automatic Hugo by default picks up the first 70 words of the content as summary and provides it to the theme.

  • Manual We can specify the summary length with a marker within the text <!--more--> at a location in the content to clip the summary at.

  • Front Matter We can use the summary variable in the front matter to supply the summary.

Note that summary is different from description. Summary in Hugo is a teaser into the content while the description is more about the content sent to search engines. The description field can only be supplied via the front matter and does not have an automatic value based on content. Some themes like eclectic give a higher priority to description and fall back to summary in the index pages.

4.2.2 Menus

The sections that we added are not linked and therefore not discoverable from the home page or any of the content pages. Hugo provides a generic way to enable content to how up in menus. There are multiple menus possible in a website. Each theme defines its own set of menus and sub-menus. Each menu has a name, like main for the main menu in the eclectic and universal themes. Each menu consists of a list of entries that form the entries for the menu.

We already have split out the menu (created in chapter 2) on the config file in section 4.1. Let’s go over the menu.yaml that we created. Each menu entry consists of multiple fields. The identifier is able to uniquely find an entry. The weight field is used to order the entries. The url field provides the relative path to the page from the website base. Hugo is flexible with the other fields and their purpose. Some themes use the name field for the display text of the menu entry and others use title. To create sub-menus, we need to provide the parent field with the identifier to the parent menu entry.

Listing 4.1. Menu entries using the config file.

main:                                       #    - Name of the menu
  - identifier: about                       #       - Identifier
    name: About                             #       - Display name (theme specific key)
    url: /about                             #       - Target URL
    weight: 1                               #       - Sorting order
  - identifier: contact
    name: Contact
    url: /contact
    weight: 2

copy

There are two ways to include pages in the website menus using Hugo. The first approach is to add menu items in the config file. This approach helps localize and tightly monitor the menu. The other approach is to specify the menus the page belongs to within its front matter. This way the menu becomes more dynamic and controlled by the content. If we delete a page, the menu automatically gets trimmed and we do not have to worry about cleaning up stale links.

The theme Eclectic has two menus, the main menu and the footer menu. For the Acme Corporation website, lets add a menu entry within the _index file for the blog section, the community section and add the terms of use and privacy policy to the footer. We also enable the footer menu by adding menu: true in the config/_default/params.yaml for the footer section.

Listing 4.2. Menu entry for the blog section

menu:
  main:
    name: Blog
    identifier: blog
    weight: 1.5
  footer:
    name: Blog
    weight: 1

copy

Listing 4.3. Menu entry for the community blog sub-section

menu:
  main:
    name: Community
    parent: blog

copy

Listing 4.4. Menu entry for the terms of use

menu:
  footer:
    name: Terms of Use
    weight: 2

copy

Listing 4.5. Menu entry for the terms of use

menu:
  footer:
    name: Privacy Policy
    weight: 3

copy

Figure 4.5. Blog for page with the main menu, submenu and the footer menu. (Code section 3)

CONTENT MANAGEMENT VIA A GUI

For content authors and editors that require a graphical environment to manage content without losing the benefits of performance with static site builder like Hugo, there are graphical wrappers like Forestry.io or Netlify CMS which wrap Hugo to provide a content management system with graphical forms instead of YAML and Markdown. Tools like these can help teams with a diverse set of requirements to come on board and get the benefits of Hugo. Non-technical team members who prefer a graphical form can get the benefits of the JAM stack with a comfortable GUI on the top. These forms place the files in the right locations and update the appropriate entries in the front matter and the config. We can use them interchangeably with directly editing the files.Tour livebook

Take our tour and find out more about liveBook's features:

  • Search - full text search of all our books

  • Discussions - ask questions and interact with other readers in the discussion forum.

  • Highlight, annotate, or bookmark.

take the tour

4.3 Better together with Page Bundles

A common problem with website source codes is the scattering of the content across databases, file systems and third-party locations. The images associated with the page live in a different location that makes it very difficult to properly clean up after the page is removed from the website. The pages are not portable across websites and the authors need access to multiple places to create them.

Hugo attempts to make contents of a web page more self-contained. The menu field in the front matter that we just learned is one of the features that Hugo has to do this. Front matter menus allow each page to own its menu entries so that it can be added/removed independently. Another feature to enable this independence is Page Bundles. Page Bundles are a collection of resources, both textual and non-textual (like images, pdf files and fonts) that are sufficient to represent pages. Page Bundles can be independently placed/removed from a Hugo website to add the associated web pages. There are two main types of Page Bundles - The Leaf and the Branch Bundles. Apart from these we also have less commonly used Headless Bundles.

4.3.1 Leaf Bundles

Leaf Bundles are a collection of textual and non-textual elements that are needed to independently represent the core contents of a single web page. This includes the markup, the metadata and the resources (images, pdf files etc.) that are specific to the page. These may also include page specific CSS and JS files.

We can convert any web page in Hugo to a leaf bundle by creating a folder in place of the markup file with the same name as the markup file and moving the markup file inside it. All the resources that are specific to the web page should be moved within this folder and the original markup file should be renamed to index. The web page can use any assets within this folder and its sub-folders including images, pdf files, metadata (yaml/toml/json files). A leaf bundle can be independently moved to a different Hugo website and should provide everything needed to render it correctly.

A leaf bundle can have multiple markup files, but the index file will not convert to a special listing page and it will not have direct access to other sub-pages.

The independence of the leaf bundle provides content creation and management capabilities that are not present in most other static site builders. Two authors can work on content independently in their own branches and the likelihood of a merge conflict is minimal. A stripped-down version of the website without any of the content can easily be created by emptying the content folder and then used for content creation with minimal compilation overhead and full support for previewing. Contractors can be assigned to develop content using a generic Hugo theme and they can submit their leaf bundle to the main website and integration effort would be minimal.

The about page in the Acme Corporation Website is the perfect page to turn into a leaf bundle. The draw.png image used inside the page is not used anywhere and therefore should be localized to the page. We will create a folder name about and move draw.png and about.md to this folder and rename about.md to index.md. To have a proper page bundle, the image should be now relative to the web page and should be referred from inside it. The path in the img tag needs to be replaced with the local one from /image/draw.jpg to draw.jpg. We could have alternatively created an image folder inside of the leaf bundle and set the path to image/draw.png if we desired to keep the image resources in such folder. We can navigate to localhost:1313/about and verify that the page looks exactly the same as before.

It is not necessary for leaf bundles to have more files than index.md. We can convert any page to a bundle by creating a folder and moving the md file within it and calling it index. We can convert blog/tropical-triangles to a page bundle by creating a folder and moving the original file as index.html inside it.

4.3.2 Branch Bundles

Branch Bundles are a collection of resources both textual and non-textual that can represent a section of the website. Technically, the section folder created for the blog and the news section for the Acme Corporation website with their own sub-pages and a _index file fits the definition of a branch bundle. To meet the definition in spirit, all the resources required by the listing page should also be present in the folder. An ideal branch bundle will contain page bundles for all the pages in the section, the _index file and the resources referred in the index page.

The objective of the independent branch bundle is same as that of a leaf bundle - to allow sections to be dropped into the website to become functional with no other change in the website. A branch bundle should ideally set its own menu entries, provide all the assets that are referred in the branch and be good to go.

REUSE AND PAGE BUNDLES

Page bundles are meant for isolation and not reuse. If the desire is to reuse images, they need to be placed in the top-level assets or the static folder. This does not mean page bundles cannot be used. Mixing and matching with one off images in page bundles and one-time images in top level asset bundles are allowed. It is advisable to avoid static as Hugo does not process (no optimization) from the static folder. The cost of sharing resources is the extra cleanup and integration effort. There are bandwidth and storage savings with reuse. The trade-off is a choice left to the web developer and should be made on a case by case basis.

For Acme Corporation’s website, we do not have any resources in the _index pages. We will add an image(news.jpg) to news section and turn that into a proper branch bundle be referencing it (By adding ![](news.jpg) to _index.md). Branch bundles are represented as places from where the website branches into one or more pages. Note that in an edge case where a branch bundle is yet to add pages, it will show up as leaf in a sitemap though Hugo will treat it as a branch and show and empty list of child pages when it renders.

Figure 4.6. Branch and leaf bundles in Hugo. On the left is the folder structure after creating page bundles. The right side shows the sitemap. Branch bundles show up as nodes that can have children in the sitemap while leaf bundles are end nodes within the sitemap just like a regular page. (Code section 4)

4.3.3 Headless Bundles

Headless bundles are page bundles where in the index file’s front matter, we specify the property headless as true. Headless pages do not have their own URL and are not rendered by default. These pages are used in certain themes for storing shared data. For example, instead of choosing the params for a structured footer, if a more unstructured footer was desired, the theme author could have used a headless page bundle. The headless page with the name footer could have been requested by the theme where the markup-based content for the footer and its associated assets could have been placed. This gives the advantage of bundling assets associated with the footer together with the markup. Not many themes use headless page bundles and we will not be investing in them for the Acme Corporation.Tour livebook

Take our tour and find out more about liveBook's features:

  • Search - full text search of all our books

  • Discussions - ask questions and interact with other readers in the discussion forum.

  • Highlight, annotate, or bookmark.

take the tour

4.4 More than tags – Taxonomies

The mechanism for organizing content into sections in Hugo is meant for the physical organization of files. Physically on disk, it is easier to have individual files for each web page, neatly organized into folders and sub-folders. In many cases the logical organization that is used on the website matches the physical organization on disk and that is why Hugo by default generates the URLs with the section name as a part of the URL. But that alone may not be enough for proper content discovery. There are infinite ways to organize content and each use case is different.

As we organize content, we quickly realize the need for pages to be grouped together such that the same page is a part of multiple categories. For example, a webpage about differences between iPhone and Android could be placed in both the iPhone and Android sections within the website. Having two copies is bad for maintenance and two different URLs would cost a lot of problems as we will need to synchronize comments, social media appearances and search engine content. In this case it would be ideal to have one URL for the actual content which could show up in the index pages for both the iPhone and Android categories in the website. Apart from categories, there may other needs to have groups with special index pages. The users need support for tags to find similar content that they are looking for. Websites may need pages that have links to all posts by the author. There can be series where posts are meant to be read in a specific order predefined by the author.

Hugo understands the need for infinite ways to organize content and provides a generic solution. At the top level of content organization, Hugo allows us to define what are called taxonomies. Taxonomies are higher level constructs that can be used to group the pages by within the website. We are free to define our own taxonomies and have our content organized by whatever field we desire. Taxonomies form associations between content that generate links within them and can be used to show related pages or have index pages for a term. We are free to define the associative meaning of the taxonomy with content. We can use taxonomies for is-a type relationships like in the case of categories and also has-a type relationships like with tags.

For example, if you are building a website about movies, you would want pages with a list of movies in groups like genre which form a is-a relationship with the movie. You would also want to list movies by director, actors, music director etc. which form a has-a relationship. Both these type of cases of these would turn into taxonomies where each individual genre or actor name would become a term that could have its own index page.

By default, Hugo defines categories and tags as taxonomies. We can also group content into categories and by tags. Since tags and categories are logical constructs (that is do not match with the file system) we can have posts in multiple categories or having multiple tags.

MULTIPLE RENDITIONS OF A SINGLE PAGE

A page can have multiple URLs. This can be done via aliases in a Page’s Front Matter in Hugo. This allows the same content to be rendered multiple times. Template authors have the control to implement the user interface (UI) of the page differently for different renditions.

Having the power to do multiple copies of a page does not mean it is a good idea to actually have multiple copies. Search engines penalize multiple copies. Comments from one page do not flow into another. There is confusion amongst users about the absolute correct page of the website.

The feature to have multiple renditions should be used for cases like backwards compatibility. If you are coming from a different URL scheme or something that supported multiple URLs in the past and would like to maintain links, then aliases come in handy. Theme creators provide canonical references in their theme’s head section to prevent the search engine penalty across aliases. The other management problems with multiple copies of content remain. Hugo provides a mechanism to have a special template for aliases, but it may not be present in many themes. It is not advised to have multiple copies of a page unless absolutely needed.

Let’s work on taxonomies for the Acme Corporation website. First, let us add tags and categories to posts. These can be added directly in the front matter of the pages. This would auto-populate tags in the content and also generate a categories page.

In the index.md for tropical triangles, we will add the following:

---
title: Tropical triangles
tags: [triangle, shape, product]
categories: [shape, design]
---

copy

Eclectic chooses to show the tags in the bottom right of the post page and the categories in the index page with the summary. Hugo will also create an index page for every tag (e.g. /tags/shape) on the website as well as a top level /tags page which will list all the tags in the website. More tags are added in code section 5. The corresponding files are available in the chapter resources.

Figure 4.7. Adding tags to the posts in the front matter, and seeing them on the post page (bottom right) (Code section 5)

We will also be adding the top-level categories page to the footer menu. While we can do this via the config file, we can create a page bundle for the categories taxonomy to keep this information isolated. Let’s create a file categories/_index.md and add the following:

---
title: Categories
menu:
  footer:
    weight: 3
    name: Categories
---

At Acme corporation we produce shapes and love to talk about them. Here are the various categories of content we have at Acme.

copy

We can also create markdown pages for each individual category which could be used to provide markdown content describing that category. This can be done by creating a branch bundle at categories/<term>, e.g. by placing categories/design/_index.md we could provide information to the page generated for the design category.

Figure 4.8. Categories taxonomy page for Acme Corporation which lists all available categories the pages in them within the Acme Website (Code section 6)

Tags and categories are the default taxonomies. Acme Corporation had a series of posts around their manufacturing process which needed to be migrated to the new website. For that we need to create a new taxonomy. To create a new taxonomy, you can modify the taxonomies variable in the site config(config.yaml) or create a new file called taxonomies.yaml inside of the config/_default folder with the following content.

# config/_default/taxonomies.yaml
# Taxonomies are defined as `<singular value>:<plural value>` in Hugo
category: categories
series: series
tag: tags

copy

Hugo requires both plural and singular value for taxonomies. With this information, Hugo creates pages at <domain>/<plural value> for the index page and at <domain>/<singular value>/<term> for individual terms within the taxonomy.

With this, we can update the manufacturing process related posts in the Acme Corporation website and assign them the manufacturing series. Once we add series: [manufacturing] to the front matter for the posts, we can see the index page for series at localhost:1313/series and the manufacturing series at localhost:1313/series/manufacturing/. We can add as many series to the website as needed. It is as valid a construct for grouping content as is tags and categories.

Figure 4.9. Series taxonomy index for Acme Corporation which lists all the series available in Acme Corporation website. (Code section 7)

4.5 YouTube, Gists and other snippets via Shortcodes

As powerful as markdown is, it does not have all possible features that we may require in our content pages. Elements like those required for a YouTube videos, Github Gists, Tweets etc. are not a part of plain markdown. While we can add these as HTML, Hugo provides a better, cleaner solution - Shortcodes. Shortcodes are snippets of templates that we can include in the content files. These get replaced with the actual contents at compile time. They are equivalent to functions in the programming world. With shortcodes we can wrap reusable pieces of HTML into their own functions that will be compiled during the page compilation. This way content creators do not have to deal with the generation of the perfect HTML for a special case which can be handled at a higher level. Shortcodes can take arguments which can be supplied to the template. Shortcode authors have access to the full website configuration and all its variables, Hugo’s built in functions and the entire theme to generate the HTML.

Shortcodes can be used within Markup using double curly braces followed by HTML-like angular brackets ({{< … >}}). Shortcodes take the name of the shortcode followed by arguments. For example, we can call a shortcode called myshortcode as:

<!-- Shortcode using angular brackets -->
{{< myshortcode arg1 arg2 >}}

<!-- We can also use named arguments if supported by the shortcode -->
{{<myshortcode name1=arg1 name2=arg2>}}

<!-- The space between angular brackets and the content inside is optional -->

copy

The Acme corporation website has a teaser video which they want to incorporate in the about page of their website. In the about page we can add {{< youtube nLAVanlu5js >}} or {{< youtube id="nLAVanlu5js" >}} to get YouTube video with id nLAVanlu5js embedded. Hugo provides an in-built shortcode to render a youtube video. Note that a YouTube video’s id is available as the parameter after v in the YouTube URL, e.g. www.youtube.com/watch?v=nLAVanlu5js would be the URL for the Acme Corporation video. You can also pass parameters like autoplay to the YouTube shortcode.

Figure 4.10. YouTube video in the Acme Corporation website (Code section 8)

4.5.1 Shortcodes with content

Shortcodes can also take inner content as an argument which the shortcode author can process using the Go template language. This way shortcode creators can do processing on the passed content before rendering the final HTML. The content we pass to a Hugo shortcode can be in the form of HTML or markup.

If we pass content to a shortcode using angular braces, then it is passed as is to the shortcode. We can use HTML like opening and closing braces inside of the double curly braces to provide the opening and closing tags for the shortcodes. One popular shortcode that is used in Hugo with content is the highlight shortcode which provides syntax highlighting. While Hugo supports code fences in Markdown, code fences have a limitation of taking at the maximum a language name as a parameter. If we wish to pass a set of arguments to the syntax highlighter, we need to use the highlight shortcode. We can pass the starting line number, the highlighted lines etc., using the highlight shortcode.

{{< highlight js "linenos=table,hl_lines=3-4,linenostart=1080" >}}
/* Enjoy your work */
if (!tired()) {
    keepCoding();
} else {
    drinkCoffee();
}
{{</ highlight >}}

copy

4.5.2 Nested shortcodes

We can nest shortcodes by having a shortcode within another. The innermost shortcode is processed first and its results are shared with the outer shortcode which can use those for processing. One clever use of this feature is to see the result of a shortcode processing by passing it to highlight

// NOTE:  {{</* ... */>}} is the escape syntax for Hugo shortcodes, i.e. content within this block is not processed as a shortcode.

{{< highlight html "linenos=table,hl_lines=3-4,linenostart=1080" >}}
    <!-- Generated Youtube source code for video -->
    {{</* youtube nLAVanlu5js */>}}
    <!-- Output -->
    {{< youtube nLAVanlu5js >}}
{{</ highlight >}}

copy

4.5.3 Built in shortcodes

Hugo comes with its own built in shortcodes that are generic and can be used across multiple websites. Themes may come bundled with some shortcodes for the users. We also have community built shortcodes available which we can add to our website and use as needed. We are free to create our own shortcodes within the shortcodes folder inside the layout folder. Some of the in-built Hugo shortcodes are:

  • gist Takes the user and gist id as parameter and renders a Github gist. You can also pass a filename to render if the gist has multiple files.

  • ref Takes a file path within the Hugo website and provides an absolute link to that file. Honors overrides like url and slug within the file.

  • relref Provides the relative link to the file within the Hugo website. Works similar to ref.

  • figure Renders an image with a caption

  • tweet Renders a Tweet given an id.

  • instagram Embeds an Instagram image with and id.

  • vimeo Renders a Vimeo video from id.

  • youtube Renders YouTube vide from an id.

  • highlight Syntax highlights the provided source code.

  • param Prints a passed parameter (useful for debugging purposes as well).

Figure 4.11. Multiple ways to use built in shortcodes in Hugo - directly calling the YouTube shortcode, passing content to the highlight shortcode and nesting shortcodes in one another. (Code section 9)

4.6 Content Sharing via Custom Shortcodes

While sharing complicated logic is one use of shortcodes, we can use shortcodes to minimize copy paste effort and keep our content clean. Shortcodes can be as simple as snippets of HTML or even markdown content which need to be shared to prevent duplication. With one source of truth, managing content gets a lot easier. While creating content, if we find that we are copying and pasting some text over and over, it is a good idea to wrap that up in a shortcode so that we can call the shortcode to provide the content instead. For content sharing, we can create two types of shortcodes.

4.6.1 HTML shortcodes

We can use custom shortcodes for placing inline HTML within our markdown files by adding a HTML file in the layouts/shortcodes folder. Then we can use that file name as the shortcode to render it in the document.

We will be embellishing the about page in the Acme Corporation website by adding dividers via a shortcode. To do that, let us create a file named divider.html in the layout and add content to create a beautiful divider in plain HTML. Then we can use this anywhere within our website to place the divider in the page. We will be adding this to multiple places in the about page.

Figure 4.12. Using custom Shortcodes in the about page for Acme Corporation to create a divider (Code section 9)

4.6.2 Markup based shortcodes

While most shortcodes are written in HTML, we do have options to write short codes in the markup languages like markdown. Hugo convert the markdown shortcodes to HTML if we call them by using percent signs % instead of angular brackets like we do in regular shortcodes. This way we can move the common data in a shared place and then use that from everywhere it is needed.

In the Acme Corporation website, we can move the product information table from the about page into its own shortcode and share it in the blog post about the manufacturing process. To do that lets create a file named productInfo.html inside the layouts/shortcodes folder and move over the contents of the product information to this page. Then we can place the text {{% productInfo %}} anywhere within our content to get the product information table. Let us do that for the about page and the manufacturing process pages in the acme corporation website.

Figure 4.13. Sharing shortcodes across multiple pages on the Acme Corporation website. On the left is the source code for the shared shortcode productInfo.html and the its two invocations at about.md and process-1.md. On the right are the renditions for about us and the manufacturing process part I pages. (Code section 10)

4.6.3 Inline shortcodes

The shortcodes we have built so far are declared in a separate file (like productInfo.html) which is available globally and shared within the entire website. Alternatively, if there is a desire to have a shortcode which is specific to the page, we can declare the shortcode within the markdown content of the page and use it within that page. This shortcode can do everything a regular shortcode can but it does not create variables outside the page and therefore speeds up compilation alongside keeping the global list of shortcodes clean. These shortcodes are called inline shortcodes as they are declared inline within the file where they are invoked. Inline shortcodes are disabled by default since shortcodes can access the entire website configuration. If all content is coming from a trusted source, this is not an issue and we can enable inline shortcodes by adding enableInlineShortcodes: true to the config. Once we do that, we can declare a shortcode within content using the .inline after the name we want to give to the shortcode. Inline shortcodes execute automatically when they are declared and cannot be nested. We can generate both HTML and markup based inline shortcodes.

// Inline Shortcode declaration and first use. Will be executed as HTML as declared with `<`. Can also declare with `%` to execute as markdown.
// Unlike regular shortcodes these are declared inline with the content.
{{< reuse.inline >}}Reused **content**{{< /reuse.inline >}}

// Shortcode usage as HTML
{{< reuse.inline />}}

//Shortcode usage as markup
{{% reuse.inline %}}

copy

There is a lot more we can do with shortcodes. We will be exploring advanced shortcodes in chapter 5.

This chapter concludes the usage of Hugo as a Content Management System (CMS). Using the features discussed so far, and relying on an existing theme like Eclectic, we can build and maintain complicated websites. Many Hugo users do not cross beyond this point in their journey to learn Hugo. While we can do a lot in the bounds of a Hugo theme, a lot of power awaits in the Hugo template system as we get beyond a theme. In the next set of chapters, we will be building pieces of our own theme that can take data in a variety of ways including the front matter, separate files, or over the internet to create custom web pages.Tour livebook

Take our tour and find out more about liveBook's features:

  • Search - full text search of all our books

  • Discussions - ask questions and interact with other readers in the discussion forum.

  • Highlight, annotate, or bookmark.

take the tour

4.7 Summary

  • Hugo offers flexibility for editors to override global configuration variables, manage it via multiple files as well as in multiple environments. By moving configuration from a single file to a folder, we can pick and choose options based on a variety of conditions.

  • Web pages in Hugo can be organized into sections by placing the corresponding markup content into folders. These sections can be nested and typically match the URL scheme for the website.

  • We can make the content accessible using index pages for various sections and also by adding those pages to menus.

  • Hugo offers menu entries from the front matter and page bundles to keep the impact of the content isolated from the rest of the website for easier migration and integration.

  • To enable portability of content across websites, Hugo offers the capability to have self-contained and isolated data. This can be achieved by placing the menu entries within the front matter and using page bundles.

  • With Leaf and Branch bundles, we can keep all assets closer to the content. We can bundle images, page specific

  • Hugo offers the ability to logically organize the content in any way desired via taxonomies. Each taxonomy consists of terms and a page can be present in multiple taxonomies and multiple terms.

  • Taxonomies create list pages and include both is-a relationship (e.g. category) and a has-a relationship(tags) for content. We can define as many taxonomies as needed.

  • Shortcodes are means to provide snippets that can be used to extend Markdown with new features. Hugo comes bundled with shortcodes for a variety of use cases from YouTube videos to advanced syntax highlighting.

  • We can define our own shortcodes in both markup and HTML formats as well as inline within content to prevent the need to copy and paste rendering logic.

Last updated