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.
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
copy
Listing 4.3. Menu entry for the community blog sub-section
copy
Listing 4.4. Menu entry for the terms of use
copy
Listing 4.5. Menu entry for the terms of use
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:
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:
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.
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)
Get Hugo in Actionbuy print book for $49.99 $34.99
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:
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.
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
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.
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