How to build an ecommerce static site with Jekyll, Contentful, and Commerce Layer

20181106 Commerce layer-01
November 7, 2018


They say that static sites are the future of web, and we couldn't agree more. They’re fast, secure, and scalable by design. For that reason, the best digital agencies and developers are looking at static sites as a great alternative to their database-driven projects. They are realizing that ecommerce is not that dynamic and that their sites would benefit a lot from going static.

There are plenty of guides out there showing how to use static site generators (SSGs) for basic ecommerce stores.

The question is: are static sites a good alternative for enterprise-level ecommerce as well?

We definitely think so, and this tutorial aims to prove just that.

About this tutorial

By reading this guide, you will learn how to build a static site ecommerce with enterprise-level features. The resulting demo site will be a multi-country, multi-language online shop with the following characteristics:

  • Each country will have its own catalogue with custom product selection and sorting

  • Each country will have its own price list with localized currency

  • Each country will have its own inventory shipping from multiple warehouses

Instead of diving into the technical details, we will focus on the site architecture and development workflow. After learning the big picture, we recommend you explore the source code and follow the step-by-step guide that is available on GitHub.


To build our demo site, we selected some of the best tools, one for each piece of the site architecture:

It's worth mentioning that we are going to manage all content with Contentful, including the product information, categories, and catalogues. Unlike traditional platforms, Commerce Layer is a purely transactional engine that leaves content management to the CMS. This approach lets creatives and content editors design any customer experience, without locking them into any site structure or template system.

Architecture and workflow

The picture below outlines how the tools fit together in the overall architecture. As you can see, each tool is dedicated to a specific step of the pipeline, contributing to the content editing and publishing workflow.

image 0

Let's have a look at each step of the diagram:

  1. Merchants work on the product offering within Commerce Layer. SKUs - the items that are being sold - are imported into Contentful for content enrichment. Prices and stock information are not imported.

  2. Content editors get the list of available SKUs and create products within Contentful. Their job is also to assign products to categories, categories to catalogues, and catalogues to countries. Published content is built into the Jekyll site on Netlify.

  3. Developers work on the site structure, layout, and client-side functions. The code is built into a static site and deployed to Netlify. Each product variant is tagged with the related SKU code, using the data-sku-code data attribute on any DOM element. This tagging lets the client-side code interact with the Commerce Layer API in real-time. Every time the code changes, developers push the code to Github for versioning and continuous deployment.

  4. The client-side code fetches prices and inventory for each available SKU. It also adds shopping cart functionalities to the page and sends orders to Commerce Layer. Merchants fulfill orders through the OMS. Newly-created SKUs are sent to Contentful for content enrichment, feeding the same workflow.

Content model

The second step of the workflow is where content editors manage the product catalogue in Contentful. This is a key step that determines how the site works. The diagram below outlines the content model that we designed to support the multi-country, multi-language structure:

image 1

Let's describe each model, their roles and relationships:

  • Countries have one associated catalogue, and the same catalogue can be associated to many countries. The market ID attribute determines the price list, inventory model, payment methods, shipping methods and promotions that are available for that country, as defined within Commerce Layer.

  • Catalogues are sorted list of categories. This means that each country can have its own categories with custom sorting.

  • Categories can have one or more lists of products by country. Each list has its own sorting, which means the same category can have a custom product merchandising for each country. If content editors don't define any specific list for a given country, the default one will be used.

  • Products can have one or more variants. All content that is common to all their variants can be defined at the product level.

  • Variants are the items that are being sold. The SKU code attribute links them to the SKUs defined in Commerce Layer and makes the variants shoppable. In our use case, variants can have a size.

Continuous delivery

Our static site ecommerce is made of three types of data that need to be refreshed as they change: code, content, and commerce.

When code changes, developers push the changes to Github, triggering a new build on Netlify. The build command fetches all published content from Contentful (jekyll contentful) and builds the static site (jekyll build).

image 2

When content changes, content editors hit the publish button on Contentful. To trigger a new build for each publishing, we need to create a build hook on Netlify...

image 3

...and link it to the publish webhook on Contentful:

image 4

When commerce changes, we don't need to trigger any new build, since prices and inventory are dynamically fetched from the static site JavaScript.

Understanding the mix of dynamic and static data

At this point what we have is a static site that includes product information based on the content stored in Contentful. To make this site a real shop we bring in the Commerce Layer JavaScript library which enriches the HTML with commerce data.

When customers enter the site and choose a shipping country, this included JavaScript library gets an OAuth2 access token putting the related market ID in the request scope. All the subsequent requests are scoped to that market.

image 5

The market in scope determines the right price list, inventory model, promotions, payment and shipping methods as defined by the merchant within Commerce Layer. As a developer, you just need to use the right access token to get country-specific commerce data and let customers place orders without errors.

Being a static site, all pages are plain HTML files served by Netlify CDN. The only dynamic information is provided by Commerce Layer API as displayed by the graphic below (red boxes) for each relevant page.

image 6

As you can see, not all pages require the same kind of data from Commerce Layer:

  1. For each product, the category page fetches the price of the first variant

  2. The product page fetches the selected variant price and availability message

  3. The shopping bag is dynamically built from the current order line items

  4. The shopping bag preview is always populated and the "Proceed to checkout" button is linked to the current order checkout URL, that is part of the order API response.

Considering that prices generally don't change very often - unless you have some adaptive pricing mechanism in place - we could also import prices into Jekyll as YAML files and leverage Commerce Layer webhooks system to trigger a new deploy on each price change.

This would further improve the website speed by reducing the number of client-side requests to the Commerce Layer API. Pages, like the category overview page, could be almost completely static (apart from the shopping bag preview) as we wouldn't need to fetch the prices dynamically.

Placing orders

When customers proceed to checkout, they are redirected to the Commerce Layer hosted checkout application, where they can add all the required information and place the order.

image 7

This is the easiest solution to reduce the time-to-market and start selling as soon as the content catalog is ready. That said, Commerce Layer also provides APIs to create a custom tailored experience on your own site. Just keep in mind that this would require us to add some server-side components to our static site, at least to manage the authentication and safely store its client_secret.

Sum up

Static sites are a great alternative to database-driven websites. They are fast, scalable, secure and cost-effective. All amazing features that can make your ecommerce shine.

In this article, we described how some of the best tools could be combined to build the perfect stack - a.k.a. JAMstack - for static ecommerce. The separation of concerns between code, content, and commerce is pretty clear. The work of developers, content editors and merchants is perfectly orchestrated.

From a features perspective, we just scratched the surface of what we can do. There are a lot of great services that would perfectly fit in our diagram, making our static site even more dynamic.

Next steps to improve functionality with more services

Other than the fully branded checkout experience, our static site ecommerce could be improved adding the following features:

  1. Full-text search and filtering, using a tool like Algolia

  2. Customer account, with access to their order history, address books and wallets (credit cards on file) through the Commerce Layer customer APIs.

  3. Last but not least, better content. Despite focusing our demo on publishing a product catalogue, we strongly believe that commerce should be driven by content. A platform like Commerce Layer provides the security, performance and flexibility you need to sell your products with confidence. But this isn’t enough. Content must be creative, emotive, and inspiring. By using Contentful, we can enrich our content model and build a unique customer journey. With our transactional engine sorted, we can focus on elevating our content experience to a whole new level.

About the author

Don't miss the latest

Get updates in your inbox
Discover new insights from the Contentful developer community each month.
add-circle arrow-right remove style-two-pin-marker subtract-circle remove