Browsing Tag: variables

    web design

    Context And Variables In The Hugo Static Site Generator — Smashing Magazine

    02/18/2021

    About The Author

    Kristian does web development and writing as part of the team behind the Tower Git client. Based on an island in Southwest Finland, he enjoys running, reading …
    More about
    Kristian

    In this article, we take a look at the topic of context and variables in Hugo, a popular static site generator. You’ll understand concepts such as the global context, flow control, and variables in Hugo templates, as well as data flow from content files through templates to partials and base templates.

    In this article, we’ll take a close look at how context works in the Hugo static site generator. We’ll examine how data flows from content to templates, how certain constructs change what data is available, and how we can pass on this data to partials and base templates.

    This article is not an introduction to Hugo. You’ll probably get the most out of it if you have some experience with Hugo, as we won’t go over every concept from scratch, but rather focus on the main topic of context and variables. However, if you refer to the Hugo documentation throughout, you may well be able to follow along even without previous experience!

    We’ll study various concepts by building up an example page. Not every single file required for the example site will be covered in detail, but the complete project is available on GitHub. If you want to understand how the pieces fit together, that’s a good starting point. Please also note that we won’t cover how to set up a Hugo site or run the development server — instructions for running the example are in the repository.

    What Is A Static Site Generator?

    If the concept of static site generators is new to you, here’s a quick introduction! Static site generators are perhaps best described by comparing them to dynamic sites. A dynamic site like a CMS generally assembles a page from scratch for each visit, perhaps fetching data from a database and combining various templates to do so. In practice, the use of caching means the page is not regenerated quite so often, but for the purpose of this comparison, we can think of it that way. A dynamic site is well suited to dynamic content: content that changes often, content that’s presented in a lot of different configurations depending on input, and content that can be manipulated by the site visitor.

    In contrast, many sites rarely change and accept little input from visitors. A “help” section for an application, a list of articles or an eBook could be examples of such sites. In this case, it makes more sense to assemble the final pages once when the content changes, thereafter serving the same pages to every visitor until the content changes again.

    Dynamic sites have more flexibility, but place more demand on the server they’re running on. They can also be difficult to distribute geographically, especially if databases are involved. Static site generators can be hosted on any server capable of delivering static files, and are easy to distribute.

    A common solution today, which mixes these approaches, is the JAMstack. “JAM” stands for JavaScript, APIs and markup and describes the building blocks of a JAMstack application: a static site generator generates static files for delivery to the client, but the stack has a dynamic component in the form of JavaScript running on the client — this client component can then use APIs to provide dynamic functionality to the user.

    Hugo

    Hugo is a popular static site generator. It’s written in Go, and the fact that Go is a compiled programming language hints at some of Hugos benefits and drawbacks. For one, Hugo is very fast, meaning that it generates static sites very quickly. Of course, this has no bearing on how fast or slow the sites created using Hugo are for the end user, but for the developer, the fact that Hugo compiles even large sites in the blink of an eye is quite valuable.

    However, as Hugo is written in a compiled language, extending it is difficult. Some other site generators allow you to insert your own code — in languages like Ruby, Python or JavaScript — into the compilation process. To extend Hugo, you would need to add your code to Hugo itself and recompile it — otherwise, you’re stuck with the template functions Hugo comes with out-of-the-box.

    While it does provide a rich variety of functions, this fact can become limiting if the generation of your pages involves some complicated logic. As we found, having a site originally developed running on a dynamic platform, the cases where you’ve taken the ability to drop in your custom code for granted do tend to pile up.

    Our team maintains a variety of web sites relating to our main product, the Tower Git client, and we’ve recently looked at moving some of these over to a static site generator. One of our sites, the “Learn” site, looked like a particularly nice fit for a pilot project. This site contains a variety of free learning material including videos, eBooks and FAQs on Git, but also other tech topics.

    Its content is largely of a static nature, and whatever interactive features there are (like newsletter sign-ups) were already powered by JavaScript. At the end of 2020, we converted this site from our previous CMS to Hugo, and today it runs as a static site. Naturally, we learned a lot about Hugo during this process. This article is a way of sharing some of the things we learned.

    Our Example

    As this article grew out of our work on converting our pages to Hugo, it seems natural to put together a (very!) simplified hypothetical landing page as an example. Our main focus will be a reusable so-called “list” template.

    In short, Hugo will use a list template for any page that contains subpages. There’s more to Hugos template hierarchy than that, but you don’t have to implement every possible template. A single list template goes a long way. It will be used in any situation calling for a list template where no more specialized template is available.

    Potential use cases include a home page, a blog index or a list of FAQs. Our reusable list template will reside in layouts/_default/list.html in our project. Again, the rest of the files needed to compile our example are available on GitHub, where you can also get a better look at how the pieces fit together. The GitHub repository also comes with a single.html template — as the name suggests, this template is used for pages that do not have subpages, but act as single pieces of content in their own right.

    Now that we’ve set the stage and explained what it is we’ll be doing, let’s get started!

    The Context Or “The Dot”

    It all starts with the dot. In a Hugo template, the object . — “the dot” — refers to the current context. What does this mean? Every template rendered in Hugo has access to a set of data — its context. This is initially set to an object representing the page currently being rendered, including its content and some metadata. The context also includes site-wide variables like configuration options and information about the current environment. You’d access a field like the title of the current page using .Title and the version of Hugo being used through .Hugo.Version — in other words, you’re accessing fields of the . structure.

    Importantly, this context can change, making a reference like `.Title` above point at something else or even making it invalid. This happens, for example, as you loop over a collection of some kind using range, or as you **split templates into partials and base templates**. We’ll look at this in detail later!

    Hugo uses the Go “templates” package, so when we refer to Hugo templates in this article, we’re really talking about Go templates. Hugo does add a lot template functions not available in standard Go templates.

    In my opinion, the context and the possibility to rebind it is one of Hugos best features. To me, it makes a lot of sense to always have “the dot” represent whatever object is the main focus of my template at a certain point, rebinding it as necessary as I go along. Of course, it’s possible to get yourself into a tangled mess as well, but I’ve been happy with it so far, to the extent that I quickly started missing it in any other static site generator I looked at.

    With this, we’re ready to look out at the humble starting point of our example — the template below, residing in the location layouts/_default/list.html in our project:

    <html>
      <head>
        <title>{{ .Title }} | {{ .Site.Title }}</title>
        <link rel="stylesheet" href="http://www.smashingmagazine.com/css/style.css">
      </head>
      <body>
        <nav>
          <a class="logo" href="{{ "/" | relURL }}">
            <img src="http://www.smashingmagazine.com/img/tower-logo.svg">
            <img src="/img/tower-claim.svg">
          </a>
          <ul>
            <li><a href="/">Home</a></li>
          </ul>
        </nav>
        <section class="content">
          <div class="container">
            <h1>{{ .Title }}</h1>
            {{ .Content }}
          </div>
        </section>
      </body>
    </html>
    

    Most of the template consists of a bare-bones HTML structure, with a stylesheet link, a menu for navigation and some extra elements and classes used for styling. The interesting stuff is between the curly braces, which signal Hugo to step in and do its magic, replacing whatever is between the braces with the result of evaluating some expression and potentially manipulating the context as well.

    As you may be able to guess, {{ .Title }} in the title tag refers to the title of the current page, while {{ .Site.Title }} refers to the title for the whole site, set in the Hugo configuration. A tag like {{ .Title }} simply tells Hugo to replace that tag with the contents of the field Title in the current context.

    So, we’ve accessed some data belonging to the page in a template. Where does this data come from? That’s the topic of the following section.

    Content And Front Matter

    Some of the variables available in the context are automatically provided by Hugo. Others are defined by us, mainly in content files. There are also other sources of data like configuration files, environment variables, data files and even APIs. In this article our focus will be on content files as the source of data.

    In general, a single content file represents a single page. A typical content file includes the main content of that page but also metadata about the page, like its title or the date it was created. Hugo supports several formats both for the main content and the metadata. In this article we’ll go with perhaps the most common combination: the content is provided as Markdown in a file containing the metadata as YAML front matter.

    In practice, that means the content file starts with a section delimited by a line containing three dashes at each end. This section constitutes the front matter, and here metadata is defined using a key: value syntax (As we’ll see soon, YAML supports more elaborate data structures too). The front matter is followed by the actual content, specified using the Markdown markup language.

    Let’s make things more concrete by looking at an example. Here’s a very simple content file with one front matter field and one paragraph of content:

    ---
    title: Home
    ---
    
    Home page of the Tower Git client. Over 100,000 developers and designers use Tower to be more productive!
    

    (This file resides at content/_index.md in our project, with _index.md denoting the content file for a page that has subpages. Again, the GitHub repository makes it clear where which file is supposed to go.)

    Rendered using the template from earlier, along with some styles and peripheral files (all found on GitHub), the result looks like this:

    (Large preview)

    You may wonder whether the field names in the front matter of our content file are predetermined, or whether we can add any field we like. The answer is “both”. There is a list of predefined fields, but we can also add any other field we can come up with. However, these fields are accessed a bit differently in the template. While a predefined field like title is accessed simply as .Title, a custom field like author is accessed using .Params.author.

    (For a quick reference on the predefined fields, along with things like functions, function parameters and page variables, see our own Hugo cheat sheet!)

    The .Content variable, used to access the main content from the content file in your template, is special. Hugo has a “shortcode” feature allowing you to use some extra tags in your Markdown content. You can also define your own. Unfortunately, these shortcodes will only work through the .Content variable — while you can run any other piece of data through a Markdown filter, this will not handle the shortcodes in the content.

    A note here about undefined variables: accessing a predefined field like .Date always works, even though you haven’t set it — an empty value will be returned in this case. Accessing an undefined custom field, like .Params.thisHasNotBeenSet, also works, returning an empty value. However, accessing a non-predefined top-level field like .thisDoesNotExist will prevent the site from compiling.

    As indicated by .Params.author as well as .Hugo.version and .Site.title earlier, chained invocations can be used to access a field nested in some other data structure. We can define such structures in our front matter. Let’s look at an example, where we define a map, or dictionary, specifying some properties for a banner on the page in our content file. Here is the updated content/_index.md:

    ---
    title: Home
    banner:
      headline: Try Tower For Free!
      subline: Download our trial to try Tower for 30 days
    ---
    
    Home page of the Tower Git client. Over 100,000 developers and designers use Tower to be more productive!
    

    Now, let’s add a banner to our template, referring to the banner data using .Params the way described above:

    <html>
      ...
      <body>
        ...
        <aside>
          <h2>{{ .Params.banner.headline }}</h2>
          <p>{{ .Params.banner.subline}}</p>
        </aside>
      </body>
    </html>
    

    Here’s what our site looks like now:

    (Large preview)

    All right! At the moment, we’re accessing fields of the default context without any issues. However, as mentioned earlier, this context is not fixed, but can change. Let’s look at how that might happen.

    Flow Control

    Flow control statements are an important part of a templating language, allowing you do different things depending on the value of variables, loop through data and more. Hugo templates provide the expected set of constructs, including if/else for conditional logic, and range for looping. Here, we will not cover flow control in Hugo in general (for more on this, see the documentation), but focus on how these statements affect the context. In this case, the most interesting statements are with and range.

    Let’s start with with. This statement checks if some expression has a “non-empty” value, and, if it has, rebinds the context to refer to the value of that expression. An end tag indicates the point where the influence of the with statement stops, and the context is rebound to whatever it was before. The Hugo documentation defines a non-empty value as false, 0, and any zero-length array, slice, map or string.

    Currently, our list template is not doing much listing at all. It might make sense for a list template to actually feature some of its subpages in some way. This gives us a perfect opportunity for examples of our flow control statements.

    Perhaps we want to display some featured content at the top of our page. This could be any piece of content — a blog post, a help article or a recipe, for example. Right now, let’s say our Tower example site has some pages highlighting its features, use-cases, a help page, a blog page, and a “learning platform” page. These are all located in the content/ directory. We configure which piece of content to feature by adding a field in the content file for our home page, content/_index.md. The page is referred to by its path, assuming the content directory as root, like so:

    ---
    title: Home
    banner:
      headline: Try Tower For Free!
      subline: Download our trial to try Tower for 30 days without limitations
    featured: /features.md
    ...
    ---
    ...
    

    Next, our list template has to be modified to display this piece of content. Hugo has a template function, .GetPage, which will allow us to refer to page objects other than the one we’re currently rendering. Recall how the context, ., was initially bound to an object representing the page being rendered? Using .GetPage and with, we can temporarily rebind the context to another page, referring to the fields of that page when displaying our featured content:

    <nav>
      ...
    </nav>
    <section class="featured">
      <div class="container">
        {{ with .GetPage .Params.featured }}
          <article>
            <h2>{{ .Title }}</h2>
            {{ .Summary }}
            <p><a href="{{ .Permalink }}">Read more →</a></p>
          </article>
        {{ end }}
      </div>
    </section>
    

    Here, {{ .Title }}, {{ .Summary }} and {{ .Permalink }} between the with and the end tags refer to those fields in the featured page, and not the main one being rendered.

    In addition to having a featured piece of content, let’s list a few more pieces of content further down. Just like the featured content, the listed pieces of content will be defined in content/_index.md, the content file for our home page. We’ll add a list of content paths to our front matter like this (in this case also specifying the section headline):

    ---
    ...
    listing_headline: Featured Pages
    listing:
      - /help.md
      - /use-cases.md
      - /blog/_index.md
      - /learn.md
    ---
    

    The reason that the blog page has its own directory and an _index.md file is that the blog will have subpages of its own — blog posts.

    To display this list in our template, we’ll use range. Unsurprisingly, this statement will loop over a list, but it will also rebind the context to each element of the list in turn. This is very convenient for our content list.

    Note that, from the perspective of Hugo, “listing” only contains some strings. For each iteration of the “range” loop, the context will be bound to one of those strings. To get access to the actual page object, we supply its path string (now the value of .) as an argument to .GetPage. Then, we’ll use the with statement again to rebind the context to the listed page object rather than its path string. Now, it’s easy to display the content of each listed page in turn:

    <aside>
      ...
    </aside>
    <section class="listing">
      <div class="container">
        <h1>{{ .Params.listing_headline }}</h1>
        <div>
          {{ range .Params.listing }}
            {{ with $.GetPage . }}
              <article>
                <h2>{{ .Title }}</h2>
                {{ .Summary }}
                <p><a href="{{ .Permalink }}">Read more →</a></p>
              </article>
            {{ end }}
          {{ end }}
        </div>
      </div>
    </section>
    

    Here’s what the site looks like at this point:

    (Large preview)

    But hold on, there’s something weird in the template above — rather than calling .GetPage, we’re calling $.GetPage. Can you guess why .GetPage wouldn’t work?

    The notation .GetPage indicates that the GetPage function is a method of the current context. Indeed, in the default context, there is such a method, but we’ve just gone ahead and changed the context! When we call .GetPage, the context is bound to a string, which does not have that method. The way we work around this is the topic of the next section.

    The Global Context

    As seen above, there are situations where the context has been changed, but we’d still like to access the original context. Here, it’s because we want to call a method existing in the original context — another common situation is when we want to access some property of the main page being rendered. No problem, there’s an easy way to do this.

    In a Hugo template, $, known as the global context, refers to the original value of the context — the context as it was when template processing started. In the previous section, it was used to call the .GetPage method even though we had rebound the context to a string. Now, we’ll also use it to access a field of the page being rendered.

    At the beginning of this article, I mentioned that our list template is reusable. So far, we’ve only used it for the home page, rendering a content file located at content/_index.md. In the example repository, there is another content file which will be rendered using this template: content/blog/_index.md. This is an index page for the blog, and just like the home page it shows a featured piece of content and lists a few more — blog posts, in this case.

    Now, let’s say we want to show listed content slightly differently on the home page — not enough to warrant a separate template, but something we can do with a conditional statement in the template itself. As an example, we’ll display the listed content in a two-column grid, as opposed to a single-column list, if we detect that we’re rendering the home page.

    Hugo comes with a page method, .IsHome, which provides exactly the functionality we need. We’ll handle the actual change in presentation by adding a class to the individual pieces of content when we find we’re on the home page, allowing our CSS file do the rest.

    We could, of course, add the class to the body element or some containing element instead, but that wouldn’t enable as good a demonstration of the global context. By the time we write the HTML for the listed piece of content, . refers to the listed page, but IsHome needs to be called on the main page being rendered. The global context comes to our rescue:

    <section class="listing">
      <div class="container">
        <h1>{{ .Params.listing_headline }}</h1>
        <div>
          {{ range .Params.listing }}
            {{ with $.GetPage . }}
              <article{{ if $.IsHome }} class="home"{{ end }}>
                <h2>{{ .Title }}</h2>
                {{ .Summary }}
                <p><a href="{{ .Permalink }}">Read more →</a></p>
              </article>
            {{ end }}
          {{ end }}
        </div>
      </div>
    </section>
    

    The blog index looks just like our home page did, albeit with different content:

    (Large preview)

    …but our home page now displays its featured content in a grid:

    (Large preview)

    Partial Templates

    When building up a real website, it quickly becomes useful to split your templates into parts. Perhaps you want to reuse some particular part of a template, or perhaps you just want to split a huge, unwieldy template into coherent pieces. For this purpose, Hugo’s partial templates are the way to go.

    From a context perspective, the important thing here is that when we include a partial template, we explicitly pass it the context we want to make available to it. A common practice is to pass in the context as it is when the partial is included, like this: {{ partial "my/partial.html" . }}. If the dot here refers to the page being rendered, that’s what will be passed to the partial; if the context has been rebound to something else, that’s what’s passed down.

    You can, of course, rebind the context in partial templates just like in normal ones. In this case, the global context, $, refers to the original context passed to the partial, not the main page being rendered (unless that’s what was passed in).

    If we want a partial template to have access to some particular piece of data, we might run into problems if we pass only this to the partial. Recall our problem earlier with accessing page methods after rebinding the context? The same goes for partials, but in this case the global context can’t help us — if we’ve passed in, say, a string to a partial template, the global context in the partial will refer to that string, and we won’t be able to call methods defined on the page context.

    The solution to this problem lies in passing in more than one piece of data when including the partial. However, we’re only allowed to provide one argument to the partial call. We can, however, make this argument a compund data type, commonly a map (known as a dictionary or a hash in other programming languages).

    In this map, we can, for example, have a Page key set to the current page object, along with other keys for any custom data to pass in. The page object will then be available as .Page in the partial, and the other values of the map are accessed similarly. A map is created using the dict template function, which takes an even number of arguments, interpreted alternately as a key, its value, a key, its value and so on.

    In our example template, let’s move the code for our featured and listed content into partials. For the featured content, it’s enough to pass in the featured page object. The listed content, however, needs access to the .IsHome method in addition to the particular listed content being rendered. As mentioned earlier, while .IsHome is available on the page object for the listed page as well, that won’t give us the correct answer — we want to know if the main page being rendered is the home page.

    We could instead pass in a boolean set to the result of calling .IsHome, but perhaps the partial will need access to other page methods in the future — let’s go with passing in the main page object as well as the listed page object. In our example, the main page is found in $ and the listed page in .. So, in the map passed to the listed partial, the key Page gets the value $ while the key “Listed” gets the value .. This is the updated main template:

    <body>
      <nav>
        <a class="logo" href="{{ "/" | relURL }}">
          <img src="http://www.smashingmagazine.com/img/tower-logo.svg">
          <img src="/img/tower-claim.svg">
        </a>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="http://www.smashingmagazine.com/blog/">Blog</a></li>
        </ul>
      </nav>
      <section class="featured">
        <div class="container">
          {{ with .GetPage .Params.featured }}
            {{ partial "partials/featured.html" . }}
          {{ end }}
        </div>
      </section>
      <section class="content">
        <div class="container">
          <h1>{{ .Title }}</h1>
          {{ .Content }}
        </div>
      </section>
      <aside>
        <h2>{{ .Params.banner.headline }}</h2>
        <p>{{ .Params.banner.subline}}</p>
      </aside>
      <section class="listing">
        <div class="container">
          <h1>{{ .Params.listing_headline }}</h1>
          <div>
            {{ range .Params.listing }}
              {{ with $.GetPage . }}
                {{ partial "partials/listed.html" (dict "Page" $ "Listed" .) }}
              {{ end }}
            {{ end }}
          </div>
        </div>
      </section>
    </body>
    

    The content of our “featured” partial does not change compared to when it was part of the list template:

    <article>
      <h2>{{ .Title }}</h2>
      {{ .Summary }}
      <p><a href="{{ .Permalink }}">Read more →</a></p>
    </article>
    

    Our partial for listed content, however, reflects the fact that the original page object is now found in .Page while the listed piece of content is found in .Listed:

    <article{{ if .Page.IsHome }} class="home"{{ end }}>
      <h2>{{ .Listed.Title }}</h2>
      {{ .Listed.Summary }}
      <p><a href="{{ .Listed.Permalink }}">Read more →</a></p>
    </article>
    

    Hugo also provides base template functionality which lets you extend a common base template, as opposed to including subtemplates. In this case, the context works similarly: when extending a base template, you provide the data that will constitute the original context in that template.

    Custom Variables

    It is also possible to assign and reassign your own custom variables in a Hugo template. These will be available in the template where they’re declared, but won’t make their way into any partials or base templates unless we explicitly pass them on. A custom variable declared inside a “block” like the one specified by an if statement will only be available inside that block — if we want to refer to it outside the block, we need to declare it outside the block, then modify it inside the block as required.

    Custom variables have names prefixed by a dollar sign ($). To declare a variable and give it a value at the same time, use the := operator. Subsequent assignments to the variable use the = operator (without colon). A variable can’t be assigned to before being declared, and it can’t be declared without giving it a value.

    One use case for custom variables is simplifying long function calls by assigning some intermediate result to an appropriately named variable. For example, we could assign the featured page object to a variable named $featured and then supply this variable to the with statement. We could also put the data to supply to the “listed” partial in a variable and give that to the partial call.

    Here’s what our template would look like with those changes:

    <section class="featured">
      <div class="container">
        {{ $featured := .GetPage .Params.featured }}
        {{ with $featured }}
          {{ partial "partials/featured.html" . }}
        {{ end }}
      </div>
    </section>
    <section class="content">
      ...
    </section>
    <aside>
      ...
    </aside>
    <section class="listing">
      <div class="container">
        <h1>{{ .Params.listing_headline }}</h1>
        <div>
          {{ range .Params.listing }}
            {{ with $.GetPage . }}
              {{ $context := (dict "Page" $ "Listed" .) }}
              {{ partial "partials/listed.html" $context }}
            {{ end }}
          {{ end }}
        </div>
      </div>
    </section>
    

    Based on my experience with Hugo, I’d recommend using custom variables liberally as soon as you’re trying to implement some more involved logic in a template. While it’s natural to try to keep your code concise, this may easily make things less clear than they could be, confusing you and others.

    Instead, use descriptively named variables for each step and don’t worry about using two lines (or three, or four, etc.) where one would do.

    .Scratch

    Finally, let’s cover the .Scratch mechanism. In earlier versions of Hugo, custom variables could only be assigned to once; it was not possible to redefine a custom variable. Nowadays, custom variables can be redefined, which makes .Scratch less important, though it still has its uses.

    In short, .Scratch is a scratch area allowing you to set and modify your own variables, like custom variables. Unlike custom variables, .Scratch belongs to the page context, so passing that context on to a partial, for example, will bring the scratch variables along with it automatically.

    You can set and retrieve variables on .Scratch by calling its methods Set and Get. There are more methods than these, for example for setting and updating compound data types, but these two ones will suffice for our needs here. Set takes two parameters: the key and the value for the data you want to set. Get only takes one: the key for the data you want to retrieve.

    Earlier, we used dict to create a map data structure to pass multiple pieces of data to a partial. This was done so that the partial for a listed page would have access to both the original page context and the particular listed page object. Using .Scratch is not necessarily a better or worse way to do this — whichever is preferrable may depend on the situation.

    Let’s see what our list template would look like using .Scratch instead of dict to pass data to the partial. We call $.Scratch.Get (again using the global context) to set the scratch variable “listed” to . — in this case, the listed page object. Then we pass in just the page object, $, to the partial. The scratch variables will follow along automatically.

    <section class="listing">
      <div class="container">
        <h1>{{ .Params.listing_headline }}</h1>
        <div>
          {{ range .Params.listing }}
            {{ with $.GetPage . }}
              {{ $.Scratch.Set "listed" . }}
              {{ partial "partials/listed.html" $ }}
            {{ end }}
          {{ end }}
        </div>
      </div>
    </section>
    

    This would require some modification to the listed.html partial as well — the original page context is now available as “the dot” while the listed page is retrieved from the .Scratch object. We’ll use a custom variable to simplify access to the listed page:

    <article{{ if .IsHome }} class="home"{{ end }}>
      {{ $listed := .Scratch.Get "listed" }}
      <h2>{{ $listed.Title }}</h2>
      {{ $listed.Summary }}
      <p><a href="{{ $listed.Permalink }}">Read more →</a></p>
    </article>
    

    One argument for doing things this way is consistency. Using .Scratch, you can make it a habit to always pass in the current page object to any partial, adding any extra data as scratch variables. Then, whenever you write or edit your partials, you know that . is a page object. Of course, you can establish a convention for yourself using a passed-in map as well: always sending along the page object as .Page, for example.

    Conclusion

    When it comes to context and data, a static site generator brings both benefits and limitations. On one hand, an operation that is too inefficient when run for every page visit may be perfectly good when run only once as the page is compiled. On the other hand, it may surprise you how often it would be useful to have access to some part of the network request even on a predominantly static site.

    To handle query string parameters, for example, on a static site, you’d have to resort to JavaScript or some proprietary solution like Netlify’s redirects. The point here is that while the jump from a dynamic to a static site is simple in theory, it does take a shift in mindset. In the beginning, it’s easy to fall back on your old habits, but practice will make perfect.

    With that, we conclude our look at data management in the Hugo static site generator. Even though we focused only on a narrow sector of its functionality, there are certainly things we didn’t cover that could have been included. Nevertheless, I hope this article gave you some added insight into how data flows from content files, to templates, to subtemplates and how it can be modified along the way.

    Note: If you already have some Hugo experience, we have a nice resource for you, quite appropriately residing on our aforementioned, Hugo-driven “Learn” site! When you just need to check the order of the arguments to the replaceRE function, how to retrieve the next page in a section, or what the “expiration date” front matter field is called, a cheat sheet comes in handy. We’ve put together just such a reference, so download a Hugo cheat sheet, in a package also featuring a host of other cheat sheets on everything from Git to the Visual Studio Code editor.

    Further Reading

    If you’re looking for more information on Hugo, here are some nice resources:

    Smashing Editorial
    (vf, il)

    Source link

    web design

    reactive variables In GraphQL Apollo Client — Smashing Magazine

    11/27/2020

    About The Author

    Daniel Don is a software developer who loves sharing his knowledge and explaining seemingly difficult topics with relatable examples. He lives in Port Harcourt …
    More about
    Daniel

    reactive variables work with GraphQL Apollo and offer you the same functionality with Redux or Context API without the complexity or extra layers that come with these other tools. A reactive variable in GraphQL Apollo is easy to use and doesn’t have as much setup process compared to Redux.

    In this article, we will look at how to set up reactive variables, how the GraphQL cache polices come into place in defining read and writes to the cache, and provide the ability for developers to add types that exist on the client-side alone so that we can structure queries for client-side variables same way we can for remote GraphQL data. After learning more about the fundamentals of reactive variables, we will build a simple app that switches the theme of our application to either dark mode or light mode based on the value of our reactive variable. We will be looking at how to query a reactive variable, how to update the value stored in a reactive variable, and how the change in value triggers updates in components that depend on the reactive variable for certain actions to occur.

    The target audience for this article would include software developers who already use GraphqQL with state management tools like Context API or Redux and willing to explore a new pattern of handling state management in GraphQL, or GraphQL beginners who are looking for effective ways to handle globally shared local state within GraphQL without Making things too complicated with external tooling. To follow along with this, you should have an existing knowledge of ReactJS and CSS too.

    A Quick Introduction To GraphQL

    With GraphQL, you get exactly what you need, and also get the data returned as well as structured how you need it.

    “GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.”

    GraphQL website

    What Is Apollo Client In GraphQL?

    Apollo Client helps you avoid manually tracking loading and error states. It also provides the ability to use GraphQL with modern React patterns like hooks, and so on.

    “Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Use it to fetch, cache, and modify application data, all while automatically updating your UI.”

    — “Introduction to Apollo Client,” Apollo Docs

    Let’s define some terms here that you will need to understand to move forward:

    • Variable
      A variable is a name you give to an assigned memory location where a value is stored. The variable name is used as a reference to the value stored in it when you need to make use of it.
    • Reactivity
      We will explain reactivity as something that triggers change on its dependents when an update is passed to it. Like the local state in React triggers component updates, the reactive variables in Apollo GraphQL also automatically trigger component updates based on changes.

    State management is a really important part of building a modern application. Having a global state is important when different components or screens require access to the same state value and possibly trigger changes when that particular state is changed.

    In the next section, we will look at how to set up a reactive variable.

    Writing Our First reactive variable

    Here’s what a reactive variable looks like:

    import { makeVar } from '@apollo/client';
    
    const myReactiveVariable = makeVar(/** An initial value can be passed in here.**/)
    

    The makeVar is imported from Apollo Client and is used to declare our a reactive variable. The makeVar takes an initial value that the reactive variable would hold. The ease of constructing a reactive variable is amazing.

    There are two ways to read data from our created reactive variable. The easiest way is to call our declared reactive variable which we have created above, as a function without an argument:

    const variable = myReactiveVariable();
    

    Getting the value of a reactive variable is that easy. In the code block above, we declared a variable that holds our reactive variable which was called without an argument to read the data it already holds.

    We can also get the value of a reactive variable with the useQuery syntax we normally would use to fetch remote data in GraphQL. To explain how we can do this, let’s look at the Cache type and field policies.

    Type And Field Policies

    The cache type and field policies help you define how a specific field in your Apollo Client cache is read and written to. You do this by providing field policies to the constructor of inMemoryCache. Each field policy is defined inside the typePolicy that corresponds to the type which contains the field. Let’s define a typePolicy called Query and define a field policy for accessing a field called myReactiveVariable.

    import { InMemoryCache } from '@apollo/client';
    
    // Here we import our reactive variable which we declared in another
    // component
    import { myReactiveVariable } from './reactivities/variable.js';
    
    // The field policies hold the initial cached state of a field.
    export default new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            myReactiveVariable: {
              read() {
                return myReactiveVariable();
              }
            }
          }
        }
      }
    })
    

    In the code snippet above, we declared a type called Query and defined a field called myReactiveVariable. Next, we added a read function that specifies what happens when the field’s cached value is read. Here’s what happens when the myReactiveVariable field cached value is being read:

    We pass in the reactive variable we had declared in another component and imported here as the value the field returns.

    Now that we have defined our typePolicies and fieldPolicies, let us go ahead and write our query to get the value store in our reactive variable. Here’s what the query would look like:

    import { gql } from "@apollo/client";
    
    export const GET_REACTIVE_VARIABLE = gql`
      query getReractiveVariable{
        myReactiveVariable @client
      }
    `
    

    The gql template literal tag we imported from Apollo Client above is used to write a GraphQL query in Apollo client.

    The query name myReactiveVariable should match the field name declared in the field policy. If you have been using GraphQL, you will notice that this querying pattern is identical to the normal query you would write if it were to be a remote GraphQL API we were querying. The only difference is the @client placed after the field name. This instructs Apollo to resolve this particular query on the client and not on any external API.

    That’s it! We have successfully set up our first reactive variable. The process looks a little bit lengthy initially but subsequently, you can declare a new reactive variable by simply declaring the reactive variable and adding a field policy for it.

    To fetch the reactive variable, you can use the useQuery hook in any component where you need it.
    Here’s an example.

    import { useQuery } from '@apollo/client';
    import { GET_REACTIVE_VARIABLE } from 'FILE_PATH_TO_YOUR_QUERY_FILE';
    
    const {loading, error, data} = useQeury(GET_DARK_MODE);
    
    // you can track loading, error states, and data the same way with a normal query in Apollo
    

    In the above code, we imported useQuery from @apollo/client. Next, we imported the GET_REACTIVE_VARIABLE query from the file it was exported from.

    Lastly, we pass on to the useQuery hook in our query, and destructure loading, error, and data from it.

    Modifying A reactive variable

    Apollo client provides a beautiful way to modify a reactive variable – calling the function returned by makeVar and pass in a single argument to the function. The argument passed in is the new value the reactive variable will hold. Let us look at an example below where we modify our reactive variable which we declared above:

    import { myReactiveVariable } from 'PATH_TO_OUR_REACTIVE_VARIABLE_FILE'
    
    myReactiveVariable("A new value is in!");
    

    In the above code, we import myReactiveVariable and we update it by calling the variable and placing the new value inside of it.

    It is so easy to update the values of a reactive variable. Once the value in a reactive variable is updated, corresponding actions are triggered in components that depend on the variable and the user-interface is adjusted automatically.

    In the next section, we will build out a simple theme-changing application that switches themes from dark mode to light mode with a click of a button. The button changes itself based on the value of the current theme. This will help us put all that we have learned together by building out something that fully and simply illustrates the concept of reactive variables and show how the user interface is automatically triggered when the reactive variable is updated.

    Here’s what our result will look like:

    (Large preview)

    Let’s begin.

    Setup:

    First, we create a new React app.

    npx create-react-app theme_toggle
    

    Next, let’s install the necessary libraries we need for Apollo and GraphQL including the react-feather library to get our icons and react-router-dom to setup routing

    npm install @apollo/client graphql react-feather react-router-dom
    

    Once we are done with all the installations, let’s go ahead and set up our graphQL, including defining our darkMode reactive variable.

    Create a folder called graphql inside the src folder and then create a sub-folder called reactivities to house all the reactive variables. Here’s how the folder tree would look like:
    src > graphql > reactivities > themeVariable.js

    I decided to arrange our file and folder structure simulating a real-world use case so follow along.
    Let’s go ahead to declare our reactive variable in the themeVariable.js file we just created:

    import { makeVar, gql } from "@apollo/client";
    export const darkMode = makeVar(false);
    

    Next, inside the same file let’s construct our query to get our reactive variable and specify that the query should be resolved on the client-side. We can decide to create a separate folder to house all our query, especially when we have many queries in our application, but for the sake of this tutorial, we will write the query inside the same file as the reactive variable and export them individually:

    import { makeVar, gql } from "@apollo/client";
    
    export const darkMode = makeVar(false);
    
    // This is the query to get the darkMode reactive variable.
    export const GET_DARK_MODE = gql`
      query getDarkMode{
        darkMode @client
      }
    `
    

    In the above piece of code, we see how straightforward it was to declare a reactive variable with the makeVar() and passed in an initial value of false for our new variable. Next, we imported gql from Apollo client and used it in writing our query.

    Next, let’s create our cache.js file and define our type and field policies to control how variables will be queried and structured:

    Create a file called cache.js inside the graphql folder. Inside cache.js here’s how we declare our policies:

    import { InMemoryCache } from '@apollo/client';
    import { darkMode } from './reactivities/themeVariable';
    
    export default new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            darkMode: {
              read() {
                return darkMode();
              }
            }
          }
        }
      }
    })
    

    In the above code, first, we imported inMemoryCache from Apollo client, and we imported our reactive variable from the file path where we stored it.
    Next, we created a new instance of inMemoryCache and our field policy is defined inside of the typePolicy object. The code above defines a field policy for the darkMode field on the Query type.

    There’s one final step to complete our setup for Apollo for our React app, we need to create a client.js file. The client.js file is a file you’re already familiar with if you use GraphQL before now. It holds the ApolloClient constructor which would finally get passed into the ApolloProvider on a top-level file (usually the index.js file). Our client.js file should be located directly inside the src folder.

    src > client.js

    import { ApolloClient } from '@apollo/client';
    import cache from './graphql/cache';
    const client = new ApolloClient({
      cache,
      connectToDevTools: true,
    });
    export default client;
    

    Here’s what we did above. We imported ApolloClient. Next, we imported our cache from where it was previously declared. Inside our ApolloClient constructor, we passed in our cache which we imported and set connectToDevTools as true to enable us to use the Apollo Dev Tools in our browser.

    Finally, we need to pass in the new ApolloClient instance which we exported as client into ApolloProvider in our top-level index.js file inside the src folder. Open the index.js file and replace the code there with this.

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { ApolloProvider } from '@apollo/client';
    import './index.css';
    import App from './App';
    import client from './client';
    ReactDOM.render(
      <ApolloProvider client={client}>
        <App />
      </ApolloProvider>,
      document.getElementById('root')
    );
    

    In the above code block, we wrapped our App component with the ApolloProvider and passed client (which we imported) to the Apollo provider. We did this in the top-level scope so that our entire app can access the ApolloProvider and the client.

    We have successfully finished everything in the setup of Apollo and the reactive variable. You’ll notice that many things we did here were related to setting up Apollo which you would still have done even if you were using Apollo with other external API for managing context.

    Since we are done with everything we need to set up Apollo and create our reactive variable, let’s now go ahead and set up our page and routing.

    We would only have one route to a page called landingPage.jsx. Inside the src folder, create a folder called pages to house all the pages (in our case, we have just one page) and create a file called landingPage.jsx in it.

    src > pages > landingPage.jsx

    Inside our newly created page, let’s create a functional component with a h1 tag containing or heading. Here’s what will be in it.

    import React from 'react';
    
    const LandingPage = () => {
      return (
        <div
          style={{
            height: '100vh',
            backgroundColor: 'white',
            }}
        >
          <h1>Welcome to Theme Toggle Appliation!</h1>
        </div>
      )
    }
    export default LandingPage
    

    Next, let’s create our button component. Inside src, create a folder called components, and create a button.jsx file.
    src > components > button.jsx

    Inside our button component, here are the things we should import icons from react-feather, the useQuery hook from apollo/client, our query and reactive variable from the file it was exported from.

    import React from 'react'
    import { Moon, Sun } from 'react-feather';
    import {  useQuery } from '@apollo/client';
    import { GET_DARK_MODE, darkMode as reactiveDarkMode } from '../graphql/reactivities/themeVariable';
    

    Inside the button component, let’s query our GraphQL client with the GET_DARK_MODE query like how we would normally query in GraphQL with Apollo.

    ...
    
    const ButtonComponent = () => {
    
      {loading, error, data} = useQuery(GET_DARK_MODE);
    
      return (...)
    }
    
    export default ButtonComponent;
    

    Next, we want to change the buttons based on the boolean value of our reactive variable that will be returned from data. To do this, we will create two buttons and use a ternary operator to display them conditionally based on the boolean value of our reactive variable:

    ...
    
    const ButtonComponent = () => {
    
      const {loading, error, data} = useQuery(GET_DARK_MODE);
    
      return (
        <div>
          {
            data.darkMode ? (
              <button
                style={{
                  backgroundColor: '#00008B',
                  border: 'none',
                  padding: '2%',
                  height: '120px',
                  borderRadius: '15px',
                  color: 'white',
                  fontSize: '18px',
                  marginTop: '5%',
                  cursor: 'pointer'
                }}
                onClick={toggleMode}
              >
                <Sun />
                <p>Switch To Light Mood</p>
              </button>
            ) :(
              <button
              style={{
                backgroundColor: '#00008B',
                border: 'none',
                padding: '2%',
                height: '120px',
                borderRadius: '15px',
                color: 'white',
                fontSize: '18px',
                marginTop: '5%',
                cursor: 'pointer'
              }}
              onClick={toggleMode}
            >
              <Moon />
              <p>Switch To Dark Mood</p>
            </button>
            )
          } 
        </div>
      )
    }
    export default ButtonComponent;
    

    In the above code, we displayed both buttons conditionally with the ternary operator to display when the value of data.darkMode is either true or false. Our initial value as declared in our themeVariable.js is false.

    Note: Remember that we can pull out darkMode from the data because we declared it this way in our cache.js field policy.

    We added some CSS to the buttons to make them look better and also added the icons we imported from react-feather to each button.

    If you noticed we had an onClick property passed into each button which called toggleMode. Let’s declare the function above but still inside the ButtonComponent:

    ...
    
    const ButtonComponent = () => {
    
      const toggleMode = () => {
        console.log("Clicked toggle mode!")
      }
    
    return (...)
    }
    
    export default ButtonComponent;
    

    Currently, we have a console.log() inside the toggleMode function. In a later part of this article, we will come back to properly write this function to update the value of the reactive variable.

    Now let’s go back to the ladingPage.jsx file we created before now and add the button we just created:

    import React from 'react';
    import ButtonComponent from '../components/button';
    
    const LandingPage = () => {
      return (
        <div
          style={{
            height: '100vh',
            backgroundColor: 'white',
            }}
        >
          <h1>Welcome to Theme Toggle Appliation!</h1>
          <ButtonComponent />
        </div>
      )
    }
    export default LandingPage
    

    To add the button, we simply imported it into our page and added it below the h1 element we already had on the page.

    Here’s how our web app looks like at the moment.

    (Large preview)

    We are almost done building our app. Next, let’s change the background and text color of the page in the landingPage.jsx style to conditionally be black or white based on the boolean value of our reactive variable which would be toggled in the button component later. To do this, we will also use the useQuery hook to get the current value of our reactive variable.

    Our landingPage.jsx file will finally look like this:

    import React from 'react'
    import { useQuery } from '@apollo/client';
    import ButtonComponent from '../components/button';
    import { darkMode, GET_DARK_MODE } from '../graphql/reactivities/themeVariable';
    
    const LandingPage = () => {
      const {loading, error, data} = useQuery(GET_DARK_MODE);
      return (
        <div style={{ height: '100vh', backgroundColor: data.darkMode ? 'black' : 'white', color: data.darkMode ? 'white' : 'black' }}>
          <h1>Welcome to Theme Toggle Appliation!</h1>
          <ButtonComponent />
        </div>
      )
    }
    export default LandingPage
    

    Pay attention to the way we change the backgroundColor and color of the div container conditionally based on the boolean value of the reactive variable returned. We make use of a ternary operator to set the backgroundColor to black or white depending on the value of data.darkMode. The same thing should be done for the value of color. This is all we need to for the landingPage.jsx component.

    The final thing we will need to do to get our application to be working is to make our toggleMode function in the button component able to modify the reactive variable on click of the button. Let’s look at how to modify a reactive variable again, this time, in a real app example.

    Modifying A reactive variable

    As we’ve previously learned, to modify a reactive variable, all you need to do is to call the function returned by makeVar and pass in the new value inside of it. Here’s how that will work in our case:

    Go to the button component and do this:

    ...
    import { GET_DARK_MODE, darkMode } from '../graphql/reactivities/themeVariable';
    
    const ButtonComponent = () => {
    
      const toggleMode = () => {
        darkMode(!darkMode)
      }
    
    return (...)
    }
    
    export default ButtonComponent;
    

    First, we imported the GET_DARK_MODE query and the darkMode reactive variable from the file they were exported from.

    Next, we wrote an arrow function for toggleMode and called the darkMode function returned by makeVar and passed an invert of the current value it contained as the new value the reactive variable will carry when it is clicked on.

    We have our entire app powered by a reactive variable and once there’s a change to the value held in the reactive variable, every component or page dependent on that variable for an action to trigger is updated and the user interface is updated with the current changes. We escaped all the hurdles of dispatch functions and other ambiguous steps we have to follow when using other state management libraries like Redux or Context API.

    Conclusion

    Reactive variables in Apollo client give you a sweet, easy to use, easy to update, and a consistent querying pattern with querying a regular remote GraphQL API. Learning to use reactive variables for state management is a plus to you because it gives you the flexibility of choice among many tools. reactive variables would enable you to manage locally shared global state among components without the extra boilerplate that would usually come with the dominant state management libraries that already exist.

    • Check out the finished code on GitHub.
    Smashing Editorial
    (ks, ra, il)

    Source link