Create a Custom Online Store
You can customize your Jumpseller store by editing its HTML & CSS, design your online store, bring your ideas to life.
In this article we will explain how you can create a Jumpseller Theme completely from scratch, covering topics such as the folders and files structure you need to have in place, through how to upload or import it to your store, as well as how you can start creating components and subcomponents which are the building blocks that will allow you to customize a theme.
As a start you need to know that a Jumpseller theme is built using a combination of HTML, CSS, JavaScript and Liquid — Jumpseller’s templating language. If you are familiar with how Liquid works in platforms like Shopify, you will find it very similar. If not, don’t worry: we have a dedicated article to get you started with Liquid in Jumpseller.
Before uploading anything, you need to prepare your theme as a .zip file that follows a specific folder and file structure. Jumpseller will not recognize a theme that deviates from this structure, so it is important to get it right from the beginning.
You can download a very basic version of a theme to get started: Download my-custom-theme.zip
Here is what the downloaded theme looks like:
my-custom-theme/
├── assets/
│ ├── app.css
│ └── theme.js
├── components/
│ ├── header.json
│ ├── header.liquid
│ ├── footer.json
│ └── footer.liquid
├── config/
│ ├── installed-components.json
│ ├── options.json
│ ├── pages.json
│ ├── settings.json
│ ├── theme.json
│ └── variants.json
├── locales/
├── partials/
│ └── schema.liquid
└── templates/
├── layout.liquid
├── home.liquid
├── contactpage.liquid
├── error.liquid
├── searchresults.liquid
├── category/
│ └── Default.liquid
├── checkout/
│ ├── cart.liquid
│ ├── checkout.liquid
│ ├── revieworder.liquid
│ └── success.liquid
├── customer/
│ ├── account.liquid
│ ├── address.liquid
│ ├── details.liquid
│ ├── login.liquid
│ └── reset_password.liquid
├── page/
│ ├── Default.liquid
│ └── Post.liquid
├── page_category/
│ ├── Blog.liquid
│ └── Default.liquid
└── product/
└── Default.liquid
Let’s go through each folder and what it contains.
The config folder holds all the JSON files that define how your theme behaves, what options it exposes to the store owner, and how it integrates with Jumpseller’s Visual Editor. This is the most critical folder in terms of configuration.
theme.json
This is the main identity file of your theme. It tells Jumpseller who made the theme, what version it is, and what capabilities it supports. It also defines the navigation menus that the theme uses.
{
"Info": {
"name": "My custom theme",
"author": "Jumpseller",
"default_lang": "en",
"use_system_translations": true,
"version": "1.0.0",
"capabilities": [
"multicurrency",
"product_appointments",
"product_price_lists",
"product_subscriptions",
"product_wishlists"
]
},
"Menus": {
"main": {
"name": "Main Menu"
},
"footer_1": {
"name": "Footer Menu 1"
},
"footer_2": {
"name": "Footer Menu 2"
}
}
}
Note: The
capabilitiesarray tells Jumpseller which advanced features your theme supports. Only include capabilities that your theme actually implements — for example,multicurrencyif your theme handles multiple currencies in the UI.
settings.json
This file defines the color schemes (also called color bundles or color packs) that are available for your theme. Each color variant has an identifier (its display name) and an options object containing the actual color values.
{
"active": {
"color": {
"default": {
"identifier": "Content",
"options": {
"background": "#FFFFFF",
"main": "#261C15",
"secondary": "#35271D",
"links": "#007EA7",
"main_button_background": "#261C15",
"main_button_text": "#F5F5F5",
"secondary_button_background": "#6B818C",
"secondary_button_text": "#FFFFFF"
}
}
}
}
}
The color keys defined here can be used in your CSS as custom properties (CSS variables), allowing store owners to switch between color schemes directly from the Visual Editor without touching any code.
options.json
This file defines the general theme options that store owners can configure from the Visual Editor. Options are grouped into named groups, each with an icon and a set of individual options.
{
"groups": {
"option-group": {
"name": "Options",
"icon": "tint",
"options": {
"theme_option": {
"name": "Option name",
"type": "input"
}
}
}
}
}
Options can be of many different types — input, checkbox, select, and color are just a few examples. You can review the full list of available option types in this article.
Once defined here, they become accessible in your Liquid templates via the options object — for example, {{ options.theme_option }}.
variants.json
This file defines theme variants — predefined style combinations that store owners can select. For a theme starting from scratch, this file can start as an empty object:
{}
Even though it starts empty, variants.json is where you can define theme variations — predefined configurations that store owners can switch between from the Visual Editor. A common use case is offering a Light and a Dark version of your theme, where each variant can define:
options.json.This allows developers to ship multiple “looks” within a single theme, giving store owners a quick way to change the overall style of their store without having to manually reconfigure every setting.
installed-components.json
This file defines which components are installed in the theme by default and where they are placed. The two essential ones for any theme are header and footer:
{
"header": {
"type": "header",
"placement": "top"
},
"footer": {
"type": "footer",
"placement": "bottom"
}
}
The placement key tells Jumpseller whether the component lives at the top or bottom of a page, which maps to the {{ index_for_top_components }} and {{ index_for_bottom_components }} Liquid variables you will see in templates.
Beyond header and footer, this file is where you define all the components that come pre-installed when a store owner first applies your theme. For example, you could add a Slider component that gets installed on the homepage by default, or a Featured products component that appears on the product page out of the box.
These page-specific components are the ones rendered by the {{ index_for_components }} Liquid variable in each template — as opposed to the header and footer, which use the top and bottom placement variables instead.
Each entry in this file corresponds to a component that lives in the components/ folder, and those components are then referenced in pages.json to control on which page templates they appear.
In other words, installed-components.json is the source of truth for what comes bundled with the theme upon installation — it’s the developer’s way of defining the default experience a store owner gets the moment they activate the theme.
pages.json
This is a very important file — it defines which components are available on each page template in the Visual Editor. Each page type (home, product, category, etc.) represents a main template, and each one can have one or more sub-templates beneath it.
{
"home": {
"Default": {
"components": [
"header",
"footer"
]
}
},
"product": {
"Default": {
"components": [
"header",
"footer"
]
},
"Giftcard": {
"components": [
"header",
"footer"
]
}
},
"category": {
"Default": {
"components": [
"header",
"footer"
]
},
"Subcategories": {
"components": [
"header",
"footer"
]
}
}
}
Note: Checkout pages (
checkout__cart,checkout__checkout, etc.) typically have an emptycomponentsarray because they use Jumpseller’s native checkout UI and do not support the Visual Editor component system.
For example, "product" is the main template for product pages, and "Default" is its default sub-template — the one that applies to standard products. You can then add additional sub-templates like "Giftcard", which would apply specifically to products of that type. Each sub-template defines its own list of components, giving developers fine-grained control over what the Visual Editor exposes depending on which sub-template is active.
The same logic applies to other page types. For "category", "Default" covers standard category pages, while a sub-template like "Subcategories" could be used to display a different layout for categories that contain nested subcategories instead of products directly.
The templates folder contains all the Liquid files that define the HTML structure of each page of the store. The most important one is layout.liquid, which is the global wrapper where all other templates are rendered.
layout.liquid
This is the heart of your theme. It contains the full HTML document structure — the <html>, <head>, and <body> tags — and everything that needs to be present on every page of the store.
<!doctype html>
<html class="no-js" lang="{{ languages.first.locale }}" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ page_title }}</title>
<meta name="description" content="{{ meta_description | default: store.name }}">
<meta name="robots" content="follow, all">
<link rel="preconnect" href="https://images.jumpseller.com">
<link rel="preconnect" href="https://cdnx.jumpseller.com">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
{% for language in languages %}
<link rel="alternate"
{% if languages.size > 1 %}
hreflang="{{ language.locale }}"
{% endif %}
href="{{ language.url }}"
>
{% endfor %}
<link rel="canonical" href="{{ canonical_url }}">
{{ favicon }}
<!-- jQuery -->
{{ 'jquery/4.0.0/jquery.min.js' | public_asset_tag: 'defer' }}
<!-- Bootstrap -->
{{ 'bootstrap/5.3.8/bootstrap.bundle.min.css' | public_asset_tag }}
<!-- Theme Schema -->
{% render 'schema' %}
<!-- Main CSS file -->
<link rel="stylesheet" href="{{ 'app.css' | asset }}">
</head>
<body>
<script src="{{ 'theme.js' | asset }}" defer></script>
<!-- Bootstrap JS -->
{{ 'bootstrap/5.3.8/bootstrap.bundle.min.js' | public_asset_tag: 'defer' }}
{{ content }}
</body>
</html>
Some key things to note here:
{{ page_title }} — Outputs the SEO title for the current page, automatically set by Jumpseller.{{ favicon }} — Outputs the <link> tag for the store’s favicon, which is managed from the admin panel under General > Branding.{{ content }} — This is where the current page’s template content is rendered. Think of it as the “slot” where each page template gets injected into the layout.{% render 'schema' %} — Renders the partials/schema.liquid file, which outputs structured data (JSON-LD) for SEO purposes.public_asset_tag — A Liquid filter used to load shared public assets (like jQuery or Bootstrap) hosted by Jumpseller’s CDN.asset — A Liquid filter used to reference files from your theme’s own assets/ folder.Tip: JavaScript files should be loaded with the
deferattribute to avoid render-blocking. CSS files for libraries like Bootstrap can be loaded synchronously in the<head>, while your ownapp.cssshould be tailored to load as efficiently as possible.
Page Templates
Every other .liquid file inside templates/ corresponds to a specific page type in the store. Most of them follow the same simple structure using three Liquid variables:
{{ index_for_top_components }}
{{ index_for_components }}
{{ index_for_bottom_components }}
These three variables are how Jumpseller’s Visual Editor inserts components into a page:
{{ index_for_top_components }} — Renders components that are placed at the top of the page (e.g., the header component).{{ index_for_components }} — Renders the main components for that page (e.g., a product listing, a contact form).{{ index_for_bottom_components }} — Renders components placed at the bottom (e.g., the footer component).Here is a summary of all the page templates and what they represent:
| File | Page |
|---|---|
home.liquid | Store homepage |
error.liquid | 404 / error page |
contactpage.liquid | Contact page |
searchresults.liquid | Search results page |
category/Default.liquid | Product category page |
product/Default.liquid | Product detail page |
page/Default.liquid | Generic static page |
page/Post.liquid | Blog post page |
page_category/Default.liquid | Blog category listing |
page_category/Blog.liquid | Blog index page |
checkout/cart.liquid | Shopping cart |
checkout/checkout.liquid | Checkout form |
checkout/revieworder.liquid | Order review step |
checkout/success.liquid | Order confirmation page |
customer/login.liquid | Customer login page |
customer/account.liquid | Customer account overview |
customer/details.liquid | Customer personal details form |
customer/address.liquid | Customer address form |
customer/reset_password.liquid | Password reset form |
Considerations
Some customer templates use special Liquid variables instead of the standard component variables, which render the native Jumpseller forms for those actions:
customer/details.liquid → uses {{ customer_details_form }}
customer/address.liquid → uses {{ customer_address_form }}
customer/reset_password.liquid → uses {{ customer_reset_password_form }}
The customer/details.liquid file is also used for the customer registration page (e.g. your-store.com/customer/registration). Even though its primary role is letting existing customers edit their personal details, the same template is rendered when a customer wants to register, so any changes to it will affect both flows.
Checkout templates (cart, checkout, revieworder, success) do support {{ index_for_top_components }} and {{ index_for_bottom_components }}, but they are intentionally left out of the theme’s files. The behavior depends on the checkout form version defined in the store’s admin panel:
Cart page, since the rest of the checkout process uses its own built-in flow.Beyond version differences, omitting top and bottom components from checkout pages is also a deliberate UX decision. Headers and footers typically contain navigation links to many areas of the store, which can lead customers away from the checkout process.
That said, a developer can add {{ index_for_top_components }} and {{ index_for_bottom_components }} to any checkout template if the design calls for it.
For more details on checkout form versions and their differences, refer to the Checkout Options and Settings article.
Components are the building blocks of a Jumpseller theme. They are the sections that store owners can add, remove, reorder, and configure visually through the Visual Editor — without touching any code.
Each component consists of two files:
.json file — defines the component’s metadata, settings, and options..liquid file — contains the HTML/Liquid markup that renders the component on the page.Component JSON File
Here is an example of a header.json:
{
"name": "Header",
"max_usage": 1,
"required": true,
"templates_in": [
"home",
"product",
"category",
"page",
"page_category",
"contactpage",
"searchresults",
"error",
"customer__account",
"customer__address",
"customer__details",
"customer__login",
"customer__reset_password"
],
"options": {},
"traits": [
{
"type": "menu_links",
"menus": ["main"]
}
],
"presets": [
{
"name": "Header",
"category": "Core"
}
]
}
The key properties are:
name — The display name of the component in the Visual Editor.max_usage — How many times this component can be added to a single page. For a header or footer, this needs to have a value of 1.required — If true, the component cannot be removed nor deleted from the template it is assigned to, though it can be hidden.templates_in — The list of theme templates where a component can appear.options — An object defining the configurable settings for this component (fonts, colors, toggle switches, etc.).traits — Special integrations like menu_links, which connects the component to a specific store navigation menu.presets — Defines how the component appears in the Visual Editor’s component picker, including its display name and category grouping.Component Liquid File
The .liquid file for a component is where you write its actual HTML markup and Liquid logic. For a theme starting from scratch, the component Liquid files are empty — they are ready for you to build out.
For example, header.liquid could be as simple as:
<header>
{% if menu.main and menu.main.items.size > 0 %}
<nav>
{% for item in menu.main.items %}
<a href="{{ item.url }}">{{ item.name }}</a>
{% endfor %}
</nav>
{% endif %}
</header>
Components have access to the store’s Liquid objects (store, cart, customer, etc.) and to their own defined options via the options object.
Partials are reusable Liquid snippets that can be included inside templates or components using the {% render 'partial_name' %} tag. They are not components — store owners cannot add or configure them through the Visual Editor — but their output is still rendered on the page wherever they are called.
The theme comes with one partial already in place:
partials/schema.liquid
This file outputs structured data in JSON-LD format, which helps search engines understand the content of each page. It handles multiple page types using a {% case template %} block:
Product schema with pricing, availability, and offer details.Store schema with address and location data.WebSite schema with a SearchAction for Google’s sitelinks search box.category, product, page, page_category and contactpage: outputs a BreadcrumbList schema.It is rendered in layout.liquid via:
{% render 'schema' %}
Tip: You can create as many partials as you need. Common uses include: product cards, pagination blocks, social sharing buttons, or any piece of UI that repeats across multiple templates or components.
The assets folder holds your theme’s static files — CSS, JavaScript, fonts, images, and any other assets that are not Liquid templates.
At minimum, a theme has two files here:
app.css — The main stylesheet for your theme. This is where your custom styles live.theme.js — The main JavaScript file for your theme.To reference these files in your templates, use the asset Liquid filter:
<link rel="stylesheet" href="{{ 'app.css' | asset }}">
<script src="{{ 'theme.js' | asset }}" defer></script>
The locales folder stores translation files for your theme. These are JSON files that map translation keys to their translated strings, one file per language.
Jumpseller themes should be translated into the store’s supported languages. Translation strings are used in Liquid templates with the {% t 'key' %} tag.
Tip: If you set
"use_system_translations": truein yourtheme.json, Jumpseller will automatically apply its own built-in translation strings for common UI elements (like “Add to cart”, “Out of stock”, etc.), so you don’t need to define them yourself.
Once your theme files are ready and organized in the correct structure, compress the root folder into a .zip file. The compressed file should contain a single root folder (e.g., my-custom-theme/) with all the files inside it.
Then, in your Jumpseller administration panel:
.zip file and confirm.Our system will validate the structure of your .zip file and, if everything is in order, the theme will be installed. If there are any errors, it will let you know which files or configurations are missing or incorrect.
With your theme uploaded, you are ready to start building. Here are some good next steps:
header.liquid and footer.liquid to get a basic page structure visible.app.css that map to the color keys defined in settings.json.Once your theme is built, it needs to meet a set of standards to be fully compatible with Jumpseller’s features and to provide a consistent, quality experience for store owners and their customers.
The following guidelines are organized by topic and split into must-haves — requirements that are non-negotiable for a compliant theme — and nice-to-haves — recommendations that significantly improve quality but are not strictly mandatory.
Must-haves
{% style %} block in layout.liquid to output theme settings as CSS custom properties (e.g. color bundles, font choices), which is an accepted and common pattern in Jumpseller themes.//resource.com/library.js instead of https://resource.com/library.js) to prevent mixed-content warnings.Nice-to-haves
Must-haves
Nice-to-haves
Must-haves
{{ store.logo }} for the image and {{ store.url }} for the link. If no logo has been uploaded, display the store name as a text fallback using {{ store.name }}.menu.main.items wrapped in an {% if menu.main and menu.main.items.size > 0 %} guard.{{ order.products_count }}.{{ store.customers_enabled }} to check this condition.{{ store.wishlists_enabled }} to check this condition.Nice-to-haves
Must-haves
theme.json file comes with two footer menus pre-defined (footer_1 and footer_2), but developers can create and include additional ones as needed.© [Current year] [Store name]. All Rights Reserved., followed by {{ powered_by }} — a Liquid tag that automatically outputs a “Powered by Jumpseller” link pointing to the Jumpseller homepage.Nice-to-haves
Must-haves
Nice-to-haves
Must-haves
<h1> tag.<h2> tag.Nice-to-haves
Must-haves
<h1> tag.Nice-to-haves
Product Block (used in category, search, and other listing pages)
Must-haves
alt attribute containing the product name.<h3> tag.Product Page
Must-haves
ol, ul, li, strong, a, p, br, table, i, s, b, h2, h3, h4, h5, img, iframe, and text alignment.Nice-to-haves
Must-haves
{{ store.customers_enabled }} to check this condition. For more details on all available customer Liquid variables, see the Liquid documentation.{{ customer_login_url }}
{{ customer_registration_url }}
{{ customer_account_url }}
{{ customer_edit_url }}
{{ customer.add_shipping_address_url }} and {{ customer.add_billing_address_url }}
{{ customer.logout_url }}
Account page (logged-in state)
Must-haves
/customer account page must show three main blocks: contact information, shipping addresses, and billing addresses.Cart Page
Must-haves
{{ estimate_form }}.{{ coupon_form }}.{{ order.subtotal }}, {{ order.discount }}, {{ order.tax }}, {{ order.shipping_cost }}, and {{ order.total }}.{{ order.checkout_url }} for the link.Checkout Page
Must-haves
{{ checkout_form }}. This tag already includes payment methods, shipping methods, and the button to proceed to the order review page.{{ order.* }} tags as the cart page.Note: The checkout form rendered by
{{ checkout_form }}comes with minimal built-in styles. Developers are responsible for styling it viaapp.cssor any other CSS file by targeting standard HTML elements such asform,input,textarea,select, and radio buttons. Avoid writing styles that are too specific to checkout-only selectors — prefer global element styles that apply consistently across the whole theme.
Review Order Page
Must-haves
order object include: {{ order.shipping_address.formatted }} — formatted shipping address{{ order.billing_address.formatted }} — formatted billing address{{ order.email }} — customer email{{ order.phone }} — customer phone number{{ order.additional_information }} — any extra information provided at checkout{{ order.custom_fields }} — custom fields collected during checkoutFor the full list of available order variables, refer to the Liquid documentation.
Order Success Page
Must-haves
{{ order.customer.name }} and should include the order number via {{ order.id }} — for example: “Thank you, [name]! Your order #[id] has been placed successfully.”
{{ order.email }}.order Liquid tags covered in the Review Order Page section above.{{ customer_reset_password_form }}.Must-haves
<h1> tag — using {{ page.name }} for static pages and {{ page_category.name }} for page categories (e.g. blog category listings).ol, ul, li, strong, a, p, br, table, i, s, b, h2, h3, h4, h5, img, iframe, and text alignment.Default, Post, and Blog.Theme Options
Must-haves
Images
Must-haves
{{ "image.jpg" | resize: "NxN" }} to optimize load times.alt attribute describing the image content. A title attribute can also be added when a tooltip on hover is desired, but it is not required.Nice-to-haves
resize over thumb for image resizing.loading="lazy" attribute is the recommended approach and works across all modern browsers. Third-party libraries like lazysizes are an option for broader legacy browser support, but are generally not necessary.Translations
Must-haves
.) or colons (:) should sit outside of translation strings. Example: {% t 'Sort by' %}:
Note: Write translation strings with the end customer in mind. Merchant-facing helper strings (e.g.
"This product has no image") do not need to be translated.
SEO
Must-haves
<h1>, store description in <h2>, product names in <h3>.<h1>, product names in <h2>, product descriptions in <h3>.<h1>, section headings (e.g. description, reviews) in <h2>, any sub-headings within those sections in <h3>.<h1>, content headings in <h2>, sub-headings in <h3>.<h1>, description if available in <h2>, individual post titles in <h3>.<h1>, description if available in <h2>, any additional section headings in <h3>.Nice-to-haves
Follow general SEO web design best practices. Google provides two useful references:
Performance
Must-haves
Nice-to-haves
Start your free 7-day trial. No credit card required.