Hugo Tutorial: How to Build a Fast Static E-Commerce Site

Jamstack
Static Site Generators
Headless CMS

Speed and reliability are the most important factors to consider when choosing the building blocks of a new e-commerce site. A site's performance can often be a key deciding factor in a merchant making a sale.

Luckily, static sites can be easily set up and configured with high-performing modern SSGs and CMSs that offer ease of development and intuitive editing interfaces.

When the time comes to choose the stack you’ll be using for your e-commerce venture, many factors can influence your decision. But when speed is the primary concern, look no further than Hugo static site generator.

Don't take my word for it. Let's see just how fast and easy it is.

Here’s what we’ll do in this Hugo tutorial below:

  • Create a Hugo static site

  • Add e-commerce functionalities with Snipcart

  • Pair the static site to a CMS (CloudCannon)

  • Deploy the site

What is the Hugo framework?

Hugo is a popular open-source static site generator written in Go. It’s widely supported by the Jamstack community, primarily because of its reputation for speed and relative simplicity.

Hugo describes itself as “the world’s fastest static website engine”, with build times on an average site of < 1ms per page. And for an e-commerce site, where contents such as product descriptions, pricing, and calls to action are often changed, fast build times are crucial.

Distributed as a single cross-platform binary, Hugo lets you get up and running sooner, with built-in functionalities like menus, sitemaps, feeds, internationalization, and image optimization.

Hugo sites can be hosted anywhere — self-hosted or alongside your CMS on a global content delivery network (CDN). In this example, we'll host alongside our CMS, CloudCannon.

Advantages of Hugo as an e-commerce solution:

  • Hugo’s flexibility will allow you to scale your sites from small stores to enterprise-level websites. There are no real limitations to what a Hugo site can do.

  • Hugo boasts a thriving community of active and supportive users. With over 100K live sites, there are plenty of working examples to help you problem-solve.

  • Above all, it’s fast to build, meaning your development turnaround will be much shorter.

These points make it a no-brainer to use Hugo for e-commerce, especially when speed is so important. But how good can it be if the rest of our stack doesn’t offer the same level of reliability and flexibility?

It’s no easy task to find a CMS that answers those needs for both the developer and content team with features like an intuitive interface, helpful documentation, and version control.

That's where CloudCannon's content management system comes in.

What is CloudCannon?

CloudCannon logo

CloudCannon is a static, headless, and Git-based CMS, meaning the CMS itself acts as a smart layer between your Git repo and your site. It has a robust feature set, including a Visual Editing mode for non-technical content editors, page-building capabilities via components ("array structures"), and instant previews of staging sites built into the core CMS.

Content editors, merchants, marketers, and developers can work simultaneously on separate branches of a single site. CloudCannon will merge and build the site in a single workflow, with complete version control via Git.

It currently supports Hugo as well as Jekyll and Eleventy sites, with new SSG support coming in future months.

Finally, CloudCannon sites can be hosted anywhere — or, if you’d prefer, CloudCannon offers hosting solutions via a global CDN.

Hugo tutorial: static site e-commerce on top of CloudCannon

Now that we’ve introduced our stack let’s jump in the fun part, building stuff!

In this tutorial, we'll create a simple e-commerce store with the help of Hugo. We'll set up the site locally, and then we'll use CloudCannon to do all the building, editing, and hosting.

Prerequisites

1. Installing Hugo

Before getting started, you'll need to install the Hugo CLI.

Since this entirely depends on your operating system, you can refer to the official documentation. Just keep in mind that if you want to make use of Sass as we do in this demonstration, you'll need to install the extended version of Hugo.

2. Creating a new site

Now that the Hugo CLI is installed and ready to go, let's create our site. In your terminal, navigate to where you want your site to live and run the following command:

hugo new site my-snipcart-shop

Feel free to replace my-snipcart-shop with a name of your choice — just remember that we'll use this name for the rest of the guide.

Running the above command will create a folder called my-snipcart-shop, and fill it with the following:

.
├── archetypes
├── content
├── data
├── layouts
├── static
├── themes
└── config.toml

For our simple site, only the data and layouts folders will be required.

The layouts directory stores our HTML templating.

The data directory stores various configuration files that are used by the site. Only TOML, YAML, and JSON files should live in this directory.

In this tutorial, we'll use this folder to store a JSON file of our products.

The static folder is where you should store any images. Hugo will copy files from the folder into the built site without changing them, hence the static name.

Now that our site has been created let's turn it into a Git repository.

In your terminal, navigate to the my-snipcart-shop directory, and run:

git init
git checkout -b main

(As of git version 2.28.0, this can be done with the single command git init -b main)

This will initialize the folder as a git repository and set the default branch to main. We now need to hook this site up to a provider. Add your repository to GitHub, GitLab, or Bitbucket, then commit and push your files.

2.1 Connecting your site to CloudCannon

You can do this at any point during your development. Changes you make in CloudCannon will be pushed to your Git provider, and any changes you push locally will be synced with CloudCannon. If you are not using Git, you can do this step now and make all your changes in the CloudCannon editor, or you can finish editing your site and add it to CloudCannon afterward.

Now let's hook this site up to CloudCannon. If you haven't already, create a CloudCannon account — generous free developer plans are available, so you won't need to pay a cent.

Log into your CloudCannon account, and select your organization if prompted.

On the left of your browser, click the tab called 'Sites'.

Click the "Add New Site" button, and select "Connect your own files" from the dropdown.

Give your site a name. This is purely for identification in CloudCannon and can be changed later.

Now it's time to choose the file source. If your site is connected to a Git provider, select your provider from the dropdown, and authenticate your account. Once logged in, select your repository from the dropdown. We'll need to choose our branch, so click the 'Use Existing' tab, and select main from the dropdown.

If you aren't using Git, select "upload your files" and upload the folder containing your site.

Once our files have synced with CloudCannon, we'll be able to choose your build options. We don't need to change anything just yet, so let’s click the 'build' button in the top right.

After a few seconds, our site will be built and set up with CloudCannon!

At the top left of your screen, you'll see a link that looks like [adjective]-[noun].cloudvent.net. This is a secure 'stable domain' generated by CloudCannon and hosts your site’s most recently built version. We can open this link in any browser, on any device, without having to log in to CloudCannon, so we can even see what your site looks like on your phone!

Note that these stable domains have preconfigured robots.txt files, meaning while your sample site can be shared with clients, search engines won't index it.

In this example, the stable domain for our site is https://airy-nature.cloudvent.net.

Click on the Files tab to browse all your files.

Because Git doesn't push empty folders, you may only see some of your files at this point. If you choose to do your editing in CloudCannon, you may need to add some folders (e.g., layouts ) as required.

2.2 Alternative: using the build-in Hugo server

If you don't want to connect to CloudCannon just yet, but still want to see a preview of your site as you build it, you can instead run this command within the folder of your site:

hugo server -D

This will listen to your files and build your site when you make changes. You can view the built version of your site by going to http://localhost:1313 in your browser.

Please be aware that if you change your config.toml you will need to stop the server, rebuild the site, and rerun the server.

3. Defining layout structure

Now that we've got all the setup done let's start defining our HTML structure. To do this, in your layouts folder, make a folder called _default, and add a file called baseof.html to it. This aptly named file will be the backbone of all our pages.

Let's define a straightforward structure. Copy the following into baseof.html:

<!DOCTYPE html>
​​<html lang="en">
​​  <!-- replace in part 4 -->
​​  <body>
​​    <div class="content">
​​      <!-- replace in part 7 -->
​​      <section class="page">
​​        <!-- replace in part 6 -->
​​      </section>
​​    </div>
​​
​​    <script async src="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.js"></script>
​​    <div hidden id="snipcart" data-api-key="YOUR_PUBLIC_API_KEY"></div>
​​  </body>
​​</html>

This file outlines the base structure of our site. The best part about using a static site generator like Hugo is that we won't need to copy all this over for every page we make!

​​Just above our closing </body>​ tag, we add Snipcart into the site in a hidden <div>​. Make sure to replace "YOUR_PUBLIC_API_KEY"​ with your Snipcart API key; otherwise, Snipcart won't behave correctly on our site.

You'll also notice that I’ve added comments to show where we will need to edit this file in the future. As we progress through the tutorial, we’ll replace those comments.

4. Adding metadata

Now that our base structure is set up let's add Snipcart and all our essential metadata. We'll do this using a partial. A partial is a small piece of HTML that makes up part of a web page, and we can re-use them throughout our site to quickly build up pages.

In our layouts folder, add a new folder called partials. In it, add a file called head.html . This file will house the head tag (surprise surprise) containing our site's metadata and will import the default Snipcart CSS styling. Add the following to this file:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel='shortcut icon' type='image/x-icon' href='favicon.ico' />
  <script src="<https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js>"></script>
  <link rel="preconnect" href="https://app.snipcart.com">
  <link rel="preconnect" href="https://cdn.snipcart.com">
  <link rel="stylesheet" type="text/css" href="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.css" />
  <title>CloudDeli</title>

  <!-- replace in part 8 -->
</head>

​​If you have a favicon that you’d like to use, place it in your static​ directory, and change favicon.ico​ to your icon’s filename.

With our partial created, we need to incorporate it into the baseof.html template. We'll use our first bit of Go templating to do this.

In your baseof.html file, replace <!-- replace in part 4--> with the following line:

{{ partial "head" . }}

The double-braces {{ }} indicate Go templating. Hugo will look through your partials for a file called head.html and substitute it in here when your site builds. The dot (.)passes the context to the partial. You can read more about it here, but we don't need to worry about it in this tutorial.

5. Creating products

Now let's create some products to sell!

We'll create a JSON file to store all our products. In your data folder, create a file called products.json and add the following to it:

[
  {
    "id": "STRATUS",
    "name": "Stratus",
    "price": 9.95,
    "image": "",
    "description": "Unlimited low, low clouds for one low, low price!",
    "product_url": "/"
  }
]

This defines a single product called Stratus which follows Snipcart’s product definition.

Creating each item like this could get annoying, so let's use CloudCannon to make this a lot easier.

First, make sure you've pushed any local changes to your repository, then open your site in CloudCannon.

In the sidebar, click 'data', then open up 'Products'. You'll be greeted with the data editor, populated with your one item.

Click the 'add value' button to create a new product.

CloudCannon will infer the relevant fields based on the previous entry in the array, giving you a simple form to fill out without configuring it.

Add as many products as you want and fill out the fields for each. When you're done, hit "Save" in the top right corner.

If you're adding any images, make sure they are somewhere within the static directory, and your image path should then be relative to static. If you add images via CloudCannon, they will be added to static/uploads by default.

6. Displaying products

Now that we have our products let’s display them on the page. While our baseof template defines the ‘shell’ of the page, the ‘meat’ goes into a main block.

In your baseof.html file, replace <!-- replace in part 6 --> with the following line:

{{ block "main" . }} {{ end }}

This lets the baseof template know where to situate the main block in the page.

Now, let’s define the loose structure of the main page. In your layouts folder, add a file called index.html, and add the following to it:

{{ define "main" }}
<main class="products">
{{ range .Site.Data.products }}
  {{ partial "product" . }}
{{ end }}
</main>
{{ end }}

​​This will be the structure of our home page. The {{ define "main" }} and {{ end }} tags let Hugo know where this block starts and ends.

Here we are introduced to the {{ range }} command. It’s Go templating’s version of a forEach loop. In this case, we pass our products data file to the loop and pass the data for each product in that file to the product.html partial. We don’t need to copy the same HTML for every product we want to display!

You might notice at this point that your build will fail. Don’t worry - it’s expected! Hugo is trying to render the “product” partial, but we haven’t actually created this file! Let’s remedy that now.

In your layouts/partials folder, create a file called product.html and add the following to it:

<div class="product">
  <h2 class="product__name">{{ .name }}</h2>
  {{ with .image }}
    <img class="product__image" src="{{ . }}" alt="">
  {{ end }}
  <p class="product__description">{{ .description }}</p>
  <div class="product__button-container">
    <div class="product__price">${{ .price }}</div>
    <button
      class="snipcart-add-item buy-button"
      data-item-id="{{ .id }}"
      data-item-name="{{ .name }}"
      data-item-price="{{ .price }}"
      data-item-url="{{ .product_url }}"
      data-item-description="{{ .description }}">
      Add to cart
    </button>
  </div>
</div>

This partial will take a product from our data file and use Go templating to inject the matching fields.

If you view your site (either on the CloudCannon stable domain or on your Hugo server) we can now see our products on the page!

7. Enabling the Cart

Now we need a way for our users to interact with their shopping cart and make purchases.

Let’s make another partial. To your layouts folder, create a folder called navigation and add a file called header.html. We put this file within a folder to help distinguish it from head.html

<header class="header">
  <a class="header__branding" href="/">
    <img class="header__logo" src="logo.svg" alt="clouddeli logo"> 
    <h1 class="header__title">CloudDeli</h1>
  </a>
  <div class="header__cart">
    <a href="#" class="snipcart-checkout" style="text-decoration: none;">
      <div class="snipcart-summary">
        <span>🛒</span>
        <span class="snipcart-total-price"></span>
      </div>
    </a>
  </div>
</header>

​​If you have an image that you want to use as a logo, place it in your static​ directory, and update the filename in the src​ attribute of the <img>​ element.

In your baseof.html file, replace <!-- replace in part 7 --> with the following line:

{{ partial "navigation/header" . }}

Now our users can browse our products, add items to their cart, and check them out!

8. Styling our site

Now it’s time to embrace our creativity and give this site some pizazz!

In your partial/head.html file, replace <!-- replace in part 8 --> with the following code:

{{ with resources.Get "sass/main.scss" }}
        {{ $style := . | resources.ToCSS }}
  <link rel="stylesheet" href="{{ $style.Permalink | relURL }}" type="text/css">
{{ end }}

The resources.Get command will look in your site’s assets directory for a file named sass/main.scss. The with command means the inner block will only run if sass/main.scss exists. If the file is found, then we’ll use Hugo pipes to pass it to the resources.ToCSS function, which converts its contents to plain CSS.

We then tell our site to use that CSS as a stylesheet.

Now, create a folder called assets at the root of your site. To this folder, add a new folder called sass and add main.scss

Open up main.scss and style your site as you please. Check out our demo site for inspiration!

9. Deploying

9.1 Hosting your site on CloudCannon

First, we need a domain name. If you don’t have one, you can buy one from a registrar.

Then in CloudCannon, go to your site's settings. Under Hosting, click 'domain'. Add your domain to the field labeled 'Domain Name', and click 'Add Domain'.

Your domain will now be linked to CloudCannon, and CloudCannon will attempt to host your built site on that domain! If there is anything else you need to do (such as configure DNS records), CloudCannon will let you know and walk you through it.

9.2 Alternative: deploying your site somewhere else

If you don't want to use CloudCannon's hosting, you can configure your site to push the built files to a separate repository. You can then connect that repository to your favorite hosting service.

First, create an empty repository with a Git provider (e.g. Github, Bitbucket, GitLab) to house your built site.

Then, navigate to your site settings, and under Files, click output syncing Select your repository and choose an existing branch or create a new branch to output to.

Click “connect and sync” at the top right of the screen. Once connected, CloudCannon will push your built site (i.e., anything that Hugo puts in the public folder) to this repository and will do so each time your site builds in CloudCannon.

You can then connect this repository to another provider. Easy!

Live demo & GitHub repo

See the Github repo here

See live demo here

Closing thoughts

Overall, building an e-commerce store with Hugo, Snipcart, and CloudCannon was straightforward and quick.

It's particularly helpful to see CloudCannon inferring relevant fields for subsequent products. Together with Hugo's fast builds and Snipcart's responsive and highly customizable shopping solutions, this sample site would be a breeze to maintain as either a developer or a merchant — or both!

If creating your site from scratch seems daunting, CloudCannon has its own Hugo e-commerce template with Snipcart integrated by default. Play around with it and explore the rest of CloudCannon’s features!

About the author

Nathan Kennedy
Software Engineer at CloudCannon

Nathan Kennedy is a Software Engineer at CloudCannon and has an honours degree in Computer Science from the University of Otago. When he’s not developing web apps, you’ll probably find him in the kitchen cooking up increasingly adventurous creations.

You can follow him on Twitter and GitHub.

36 000+ geeks are getting our monthly newsletter: join them!