Jekyll E-commerce with Snipcart: Add a Shopping Cart to Your Static Site
Learn how to add a fully functional shopping cart to a Jekyll site using Snipcart. Product listings, checkout, payments, and order management — no backend required.
Jekyll is a static site generator — it does not have a database, a server, or a shopping cart. But that does not mean you cannot sell online. Snipcart is a JavaScript-powered shopping cart that adds to any static site with a few lines of HTML. No backend, no database, no hosting complexity.
Here is how to build a fully functional Jekyll store with Snipcart.
What is Snipcart?
Snipcart is a headless e-commerce platform. You add their JavaScript to your site, mark your products with HTML data attributes, and Snipcart handles the cart, checkout, payment processing (via Stripe), and order management through their dashboard.
You keep full control of your site’s design. Snipcart handles everything that requires a server.
Pricing: 2% transaction fee on sales up to $500/month, then $20/month flat fee. Free to test with a sandbox mode.
Setting up Snipcart in Jekyll
Step 1: Create a Snipcart account
Sign up at snipcart.com. You will get a public API key for testing (sandbox) and one for production.
Step 2: Add Snipcart to your Jekyll layout
Add the following to your _layouts/default.html before the closing </body> tag:
{% if site.snipcart_key != "" %}
<link rel="preconnect" href="https://app.snipcart.com">
<link rel="preconnect" href="https://cdn.snipcart.com">
<link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.3.3/default/snipcart.css" />
<div hidden id="snipcart" data-api-key="{{ site.snipcart_key }}"></div>
<script async src="https://cdn.snipcart.com/themes/v3.3.3/default/snipcart.js"></script>
{% endif %}
Step 3: Add your API key to _config.yml
# _config.yml
snipcart_key: "" # Add your Snipcart public API key here
Keep your test key for local development and your live key in your hosting platform’s environment variables.
Step 4: Define products in front matter
Create a _products collection in _config.yml:
collections:
products:
output: true
permalink: /shop/:slug/
Create a product file at _products/jekyll-starter-theme.md:
---
layout: product
title: "Jekyll Starter Theme"
price: 29.00
sku: "JST-001"
description: "A clean, minimal Jekyll theme for developers. Includes dark mode, SEO optimisation, and full documentation."
image: /assets/images/products/jekyll-starter-theme.jpg
category: Themes
in_stock: true
---
Your full product description here in Markdown...
Step 5: Create the Add to Cart button
In your product layout or product listing, add a button with Snipcart’s data attributes:
<button
class="snipcart-add-item btn btn--primary"
data-item-id="{{ product.sku }}"
data-item-name="{{ product.title }}"
data-item-price="{{ product.price }}"
data-item-url="{{ product.url | absolute_url }}"
data-item-description="{{ product.description | strip_html | truncate: 255 }}"
data-item-image="{{ product.image | absolute_url }}"
>
Add to Cart — ${{ product.price }}
</button>
The data-item-url must point to the live product page where Snipcart can verify the price. This is Snipcart’s anti-fraud mechanism — it crawls the URL and confirms the price matches what was passed to the cart.
Step 6: Add the cart button to your nav
<button class="snipcart-checkout navbar__icon-btn" aria-label="Shopping cart">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"/>
</svg>
<span class="snipcart-items-count">0</span>
</button>
The snipcart-items-count class is updated automatically by Snipcart with the current cart item count.
Creating a product listing page
Create _pages/shop.html:
---
layout: default
title: "Shop"
permalink: /shop/
---
<div class="product-grid">
{% for product in site.products %}
<div class="product-card">
<img src="{{ product.image | relative_url }}" alt="{{ product.title }}">
<h3>{{ product.title }}</h3>
<p>{{ product.description | truncate: 120 }}</p>
<p class="price">${{ product.price }}</p>
<button
class="snipcart-add-item btn btn--primary"
data-item-id="{{ product.sku }}"
data-item-name="{{ product.title }}"
data-item-price="{{ product.price }}"
data-item-url="{{ product.url | absolute_url }}"
data-item-description="{{ product.description | strip_html | truncate: 255 }}"
data-item-image="{{ product.image | absolute_url }}"
>
Add to Cart
</button>
</div>
{% endfor %}
</div>
Handling digital products
Snipcart supports digital product delivery via a file URL. Add it to your button:
data-item-file-guid="YOUR_FILE_GUID"
You upload the file in the Snipcart dashboard and get a GUID. Snipcart delivers a download link to the customer after payment. This is ideal for selling theme files, ebooks, or templates.
Tax and shipping
Configure tax rules and shipping rates in the Snipcart dashboard — no code changes needed. You can set rates by country, region, or product category.
Alternatives to Snipcart
| Option | Best for |
|---|---|
| Snipcart | Most Jekyll stores — simplest setup |
| Gumroad | Digital products only — embed a Gumroad button |
| Stripe Payment Links | Single products, no cart needed |
| Shopify Buy Button | Larger catalogues with existing Shopify store |
| Lemon Squeezy | SaaS products, subscriptions, software |
Is Jekyll right for e-commerce?
Jekyll works well for stores with a small to medium catalogue (under a few hundred products), digital goods, or marketplaces where the product pages are mostly static content. It is not the right choice for large catalogues with complex inventory management, real-time stock levels, or customer accounts.
For selling a Jekyll theme or digital download, Jekyll plus Snipcart is a perfectly reasonable stack. For a 10,000-SKU physical goods store, reach for Shopify.
Customising the Snipcart checkout appearance
Snipcart’s default checkout UI is functional and works out of the box, but you can style it to match your Jekyll site’s design. Snipcart uses CSS custom properties that you override in your stylesheet:
// _sass/_snipcart.scss
:root {
--color-input-focus: #2563eb;
--color-button-primary: #2563eb;
--color-button-primary-hover: #1d4ed8;
--color-badge-background: #2563eb;
--color-cart-icon: currentColor;
}
// Override Snipcart's default font with yours
#snipcart {
font-family: var(--font-sans);
}
// Adjust the cart sidebar position and width
.snip-layout__main-container {
max-width: 480px;
}
Snipcart exposes dozens of CSS variables in their theming documentation. The most impactful changes for brand consistency are the primary colour (used for buttons and highlights), font family, and border radius.
Managing product variants
Many products have variants — size, colour, format, or licence tier. Snipcart handles variants through data-item-custom attributes:
<button
class="snipcart-add-item"
data-item-id="{{ product.sku }}"
data-item-name="{{ product.title }}"
data-item-price="{{ product.price }}"
data-item-url="{{ product.url | absolute_url }}"
data-item-custom1-name="Licence"
data-item-custom1-options="Personal[+0.00]|Commercial[+20.00]|Extended[+50.00]"
data-item-custom1-required="true"
>
Add to Cart
</button>
The options format is Label[+price_modifier] — customers select a licence tier and the price adjusts accordingly. Use this for theme licences (personal vs commercial), print sizes, or any dimension that affects price.
For products with multiple variants in Jekyll front matter:
# _products/minimal-theme.md
variants:
- name: Personal Licence
price: 29.00
sku: MT-PERSONAL
- name: Commercial Licence
price: 49.00
sku: MT-COMMERCIAL
- name: Extended Licence
price: 99.00
sku: MT-EXTENDED
{% for variant in product.variants %}
<button
class="snipcart-add-item btn"
data-item-id="{{ variant.sku }}"
data-item-name="{{ product.title }} — {{ variant.name }}"
data-item-price="{{ variant.price }}"
data-item-url="{{ product.url | absolute_url }}"
>
Buy {{ variant.name }} — ${{ variant.price }}
</button>
{% endfor %}
Order notifications and webhooks
Snipcart sends webhook events for every order lifecycle event: order.completed, order.status.changed, order.refunded. You can connect these to external services without any server infrastructure by using a serverless function or Zapier.
A common pattern: use a Zapier webhook trigger to send yourself a Slack message on every new order, add the customer to your Mailchimp list, and log the order to a Google Sheet. No server, no code — just Zapier’s workflow builder connecting Snipcart to your existing tools.
For more complex logic, a Cloudflare Worker or Netlify Function can receive the webhook and process it with custom JavaScript. The function verifies the Snipcart webhook signature, then performs actions like generating a personalised download link, triggering a transactional email, or updating a database.
SEO for product pages
Jekyll product pages generated from a collection benefit from the same SEO practices as blog posts. Each product should have a unique, descriptive title, a meta description that includes the price and primary benefit, and structured data in JSON-LD format.
Add Product schema to your product layout:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ page.title | jsonify }},
"description": {{ page.description | strip_html | jsonify }},
"image": "{{ page.image | absolute_url }}",
"offers": {
"@type": "Offer",
"price": "{{ page.price }}",
"priceCurrency": "USD",
"availability": "{% if page.in_stock %}https://schema.org/InStock{% else %}https://schema.org/OutOfStock{% endif %}"
}
}
</script>
This Product schema tells search engines that your page represents a purchasable product, potentially triggering rich results with star ratings, prices, and availability directly in search results.
For digital products, add "@type": "SoftwareApplication" (for themes and plugins) or "@type": "DigitalDocument" (for ebooks and templates) alongside "@type": "Product" using an array. This gives search engines more specific type information that may improve rich result eligibility.
Testing your Snipcart integration
Snipcart provides a sandbox environment for testing. Set your API key to the sandbox key in _config.yml during development:
snipcart_key: "YOUR_SANDBOX_PUBLIC_API_KEY"
In sandbox mode, use Stripe’s test card numbers (4242 4242 4242 4242 with any future date and any CVC) to complete test purchases. Orders appear in your Snipcart sandbox dashboard and trigger all webhooks — useful for testing your order notification workflows.
Before going live, verify: the product price in the Snipcart checkout matches what is displayed on the product page (Snipcart validates this), the checkout correctly identifies your domain, file delivery works for digital products, and tax is calculated correctly for your target markets.
Snipcart’s simplicity is its main advantage for Jekyll stores. There is no server to provision, no checkout page to build, and no payment processing integration to maintain — everything happens through a few HTML attributes and a JavaScript file. For theme creators, digital product sellers, and small physical goods shops, this is the most practical e-commerce approach available for a Jekyll site.
Building a complete product page
A well-designed product page converts better than a plain listing. For a Jekyll theme or digital product, the product page should clearly communicate value, show the product in use, address common objections, and make the purchase action unambiguous.
Structure the page with a clear layout: hero image or screenshot at top, key benefits and features below, pricing and purchase button prominent without scrolling (on desktop), and detailed description further down the page for users who want to read before buying.
For a Jekyll theme product page, the most effective structure is: live demo link prominently placed, a carousel of screenshots showing different page types (homepage, blog post, mobile view), feature list organised by category (design, performance, SEO, support), pricing with a clear statement of what the licence covers, and a FAQ section addressing the most common pre-purchase questions.
The snipcart-add-item button should appear multiple times on a long product page — at minimum near the top and at the bottom after the full description. Users who are ready to buy early should not have to scroll through the entire page to find the buy button; users who need convincing should find one at the end of the persuasive content.
Handling licences for digital products
If you sell Jekyll themes, you likely offer multiple licence tiers. The personal/commercial distinction is standard for theme sellers: a personal licence covers non-commercial use (a personal blog or portfolio), while a commercial licence covers any use that generates revenue or is for a client.
Define each licence as a separate Snipcart product (separate SKU) rather than a variant of one product. This gives you clear records in the Snipcart dashboard — you can see exactly how many of each licence type have sold, which is useful for financial reporting and understanding your customer mix.
Document your licence terms clearly on the product page and in your confirmation email. Common points to address: how many sites the licence covers, whether it permits modification and redistribution of the theme, whether it covers client projects, and what happens when the buyer wants to upgrade from personal to commercial.
Consider using Lemon Squeezy as an alternative to Snipcart for software products specifically. Lemon Squeezy is purpose-built for digital goods and software, handles EU VAT automatically (a significant compliance burden for theme sellers with European customers), and provides built-in licence key generation and management. The trade-off is less design control over the checkout experience compared to Snipcart, which embeds directly in your Jekyll site. For most theme sellers, the VAT handling alone makes Lemon Squeezy worth serious consideration if you have European customers.
Connecting your store to accounting
Regardless of which payment platform you use, connecting sales data to your accounting workflow from the beginning saves significant time at tax time. Snipcart provides webhook events for every order, refund, and subscription event — these can feed directly into accounting tools via Zapier.
A typical workflow: Snipcart order → Zapier → QuickBooks (create invoice) + Google Sheets (log row) + Slack (notify). This gives you real-time notifications of sales, automatic invoice creation, and a spreadsheet log for reconciliation — all without writing any server-side code.
For VAT-registered businesses, the most important data point to capture from each order is the buyer’s country. Snipcart’s order objects include billing address country, which you can log to your spreadsheet and use to segregate EU sales (subject to VAT MOSS reporting) from non-EU sales. Getting this right from the start avoids the painful exercise of retroactively categorising hundreds of orders when preparing a VAT return.
Selling digital products through a Jekyll site removes the traditional barriers to a software business: no inventory, no fulfilment, no warehouse, no payment terminal. The combination of Jekyll’s static site performance, Snipcart’s embedded checkout, and Stripe’s payment processing gives you a professional online store that costs nothing to host and processes payments reliably at scale. Browse Jekyll themes on JekyllHub to find a commerce-ready theme that already has product page layouts, grid displays, and cart button styling built in.