# 4. Content Management with Hugo

#### This chapter covers:

* Organizing Pages in a Hugo website into sections, menus and Hugo taxonomies<br>
* Bundling contents of a page into a Page Bundle<br>
* Using Hugo’s built in and community provided shortcodes to get more features in markdown.<br>

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 <a href="#heading_id_3" id="heading_id_3"></a>

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)**

![config](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/config.png)

**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.<br>
* **Aegints** Bn timrnoenven rjwb fcf odmpelcte ruetfesa neurytcrl nrdeu edaauomtt ncg lnaaum stgenti.<br>
* **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.<br>
* **Acvr** C ilcbpu kgt-reeslae hihwc ja reayd rv xy fojx ewhre eovyerne ja vedinti vr rviyef hiert sokwflrow.<br>
* **Ecntoiuodr** Bob urenrtc vv/ceileiat ervntnmoine evesrd rk rbx nyx ressu.<br>

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)**

![debug](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/debug.png)

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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| <p><strong>Bob</strong> 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. <em>(Gets a look at Gabby’s screen)</em> What’s that?</p><p><strong>Gabby</strong> That’s the end of hocus pocus. All the config as code, properly versioned and checked in. No more of the guessing game.</p><p><strong>Bob</strong> 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.</p><p><strong>Gabby</strong> 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.</p><p><strong>Bob</strong> I can’t give you a private stack. You know how much that costs. That’s crazy.</p> |

Sign in for more free preview timesign in now

### 4.2  Organizing content with sections and menus <a href="#heading_id_4" id="heading_id_4"></a>

#### 4.2.1  Sections <a href="#heading_id_5" id="heading_id_5"></a>

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`](https://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`](https://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)**

![sections](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/sections.png)

Gvw xw zns taaveign xr [`localhost:1313/blog`](http://localhost:1313/blog) xr vzv rvp sopst nj vrq `blog` sinecot gnz [`localhost:1313/blog/community`](http://localhost:1313/blog/community) rv xoc qrk otsps nj rkg tobcisneus xtl ontymumic rpodived ostps usn [`localhost:1313/news`](http://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)**

![blog](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/blog.png)

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.<br>
* **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.<br>
* **Front Matter** We can use the `summary` variable in the front matter to supply the summary.<br>

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 <a href="#heading_id_6" id="heading_id_6"></a>

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)**

![menu](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/menu.png)

**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 href="#heading_id_7" id="heading_id_7"></a>

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 <a href="#heading_id_8" id="heading_id_8"></a>

*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`](http://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 <a href="#heading_id_9" id="heading_id_9"></a>

*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)**

![bundles](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/bundles.png)

#### 4.3.3  Headless Bundles <a href="#heading_id_10" id="heading_id_10"></a>

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 <a href="#heading_id_11" id="heading_id_11"></a>

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)**

![tags](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/tags.png)

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)**

![categories](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/categories.png)

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](http://localhost:1313/series) and the manufacturing series at [localhost:1313/series/manufacturing/](http://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)**

![series](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/series.png)Get Hugo in Actionbuy print book for  $49.99 $34.99

### 4.5  YouTube, Gists and other snippets via Shortcodes <a href="#heading_id_12" id="heading_id_12"></a>

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](https://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)**

![video](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/video.png)

#### 4.5.1  Shortcodes with content <a href="#heading_id_13" id="heading_id_13"></a>

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 <a href="#heading_id_14" id="heading_id_14"></a>

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 <a href="#heading_id_15" id="heading_id_15"></a>

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.<br>
* **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.<br>
* **relref** Provides the relative link to the file within the Hugo website. Works similar to ref.<br>
* **figure** Renders an image with a caption<br>
* **tweet** Renders a Tweet given an id.<br>
* **instagram** Embeds an Instagram image with and id.<br>
* **vimeo** Renders a Vimeo video from id.<br>
* **youtube** Renders YouTube vide from an id.<br>
* **highlight** Syntax highlights the provided source code.<br>
* **param** Prints a passed parameter (useful for debugging purposes as well).<br>

**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)**

![shortcodes](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/shortcodes.png)Sign in for more free preview timesign in now

### 4.6  Content Sharing via Custom Shortcodes <a href="#heading_id_16" id="heading_id_16"></a>

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 <a href="#heading_id_17" id="heading_id_17"></a>

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)**

![cs](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/cs.png)

#### 4.6.2  Markup based shortcodes <a href="#heading_id_18" id="heading_id_18"></a>

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)**

![sharedcodes](https://dpzbhybb2pdcj.cloudfront.net/jain/v-4/Figures/sharedcodes.png)

#### 4.6.3  Inline shortcodes <a href="#heading_id_19" id="heading_id_19"></a>

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 <a href="#heading_id_20" id="heading_id_20"></a>

* 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.<br>
* 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.<br>
* We can make the content accessible using index pages for various sections and also by adding those pages to menus.<br>
* 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.<br>
* 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.<br>
* With Leaf and Branch bundles, we can keep all assets closer to the content. We can bundle images, page specific<br>
* 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.<br>
* 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.<br>
* 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.<br>
* 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.
