Widgets
Widgets are the building blocks of your theme pages. Each widget is a self-contained component with its own settings, template, and optionally repeatable blocks.
Widget Folder Structure
Each widget has a dedicated folder inside widgets/:
widgets/
├── hero-slider/
│ ├── schema.json # Widget configuration & settings
│ └── widget.njk # Widget HTML template (Nunjucks)
├── testimonials/
│ ├── schema.json
│ └── widget.njk
└── product-details/
├── schema.json
└── widget.njk
Generating a New Widget
To create a new widget and add it directly to a specific page:
qumra gen widget <widget-name>
You'll see a list of available pages to add the widget to:
? Select the page to add the widget to:
❯ index.json
product.json
collection.json
cart.json
blog.json
After selection:
✅ Widget '<widget-name>' created and added to '<page>.json' successfully.
Widget names must be lowercase with only letters, numbers, and dashes (e.g., hero-slider, product-grid).
Widget Schema (schema.json)
The schema defines the widget's name, settings, blocks, and presets.
Basic Schema Structure
{
"name": "Hero Slider",
"settings": {
"autoplay": {
"type": "boolean",
"label": "Enable Autoplay",
"default": true
},
"speed": {
"type": "number",
"label": "Transition Speed (seconds)",
"default": 5,
"min": 3,
"max": 15
},
"titleColor": {
"type": "color",
"label": "Title Color",
"default": "#ffffff"
}
}
}
Available Setting Types
| Type | Description | Additional Properties |
|---|---|---|
string | Single-line text | - |
text | Multi-line text | - |
number | Numeric input | min, max, step |
boolean | On/off toggle | - |
color | Color picker | - |
select | Dropdown selection | options: [{label, value}] |
media | Image/media selector | - |
range | Slider control | min, max, step, unit |
products | Product selector | - |
menu | Menu selector | - |
Select Example
{
"contentPosition": {
"type": "select",
"label": "Content Position",
"default": "center",
"options": [
{ "label": "Left", "value": "left" },
{ "label": "Center", "value": "center" },
{ "label": "Right", "value": "right" }
]
}
}
Range Example
{
"opacity": {
"type": "range",
"label": "Overlay Opacity",
"default": 50,
"min": 0,
"max": 100,
"step": 5,
"unit": "%"
}
}
Blocks (Repeatable Content)
Blocks allow users to add multiple items of the same type (like slides, testimonials, FAQs).
Schema with Blocks
{
"name": "Testimonials",
"settings": {
"title": {
"type": "string",
"label": "Section Title",
"default": "What Our Customers Say"
}
},
"blocks": {
"testimonial": {
"name": "Testimonial",
"settings": {
"customerName": {
"type": "string",
"label": "Customer Name",
"default": ""
},
"review": {
"type": "text",
"label": "Review Text",
"default": ""
},
"rating": {
"type": "number",
"label": "Rating (1-5)",
"default": 5,
"min": 1,
"max": 5
},
"avatar": {
"type": "media",
"label": "Customer Photo",
"default": ""
}
}
}
}
}
Using Blocks in Template
<section class="testimonials">
<h2>{{ widget.data.title }}</h2>
{% for block in widget.blocks %}
<div class="testimonial-card">
{% if block.settings.avatar %}
<img src="{{ block.settings.avatar }}" alt="{{ block.settings.customerName }}">
{% endif %}
<p class="review">{{ block.settings.review }}</p>
<div class="rating">★ {{ block.settings.rating }}/5</div>
<span class="name">{{ block.settings.customerName }}</span>
</div>
{% endfor %}
</section>
Presets (Default Configurations)
Presets provide pre-configured widget instances with sample data:
{
"name": "FAQ Section",
"settings": { ... },
"blocks": { ... },
"presets": [
{
"category": "Content",
"name": "FAQ Section",
"blocks": [
{
"block": "faq",
"name": "Shipping Question",
"settings": {
"question": "How long does shipping take?",
"answer": "Standard shipping takes 3-5 business days."
}
},
{
"block": "faq",
"name": "Returns Question",
"settings": {
"question": "What is your return policy?",
"answer": "We accept returns within 30 days of purchase."
}
}
]
}
]
}
Widget Template (widget.njk)
Access widget data in your Nunjucks template:
{# Widget-level settings #}
<h1 style="color: {{ widget.data.titleColor }}">
{{ widget.data.title }}
</h1>
{# Global theme settings #}
<div style="background: {{ settings.mainColor }}">
...
</div>
{# Iterate over blocks #}
{% for block in widget.blocks %}
<div class="block-{{ block.type }}">
{{ block.settings.content }}
</div>
{% endfor %}
Using Widgets in Pages
Add widgets to your page JSON with settings and blocks:
{
"layout": "layout",
"title": "Homepage",
"widgets": [
{
"name": "hero-slider",
"widget": "hero-slider",
"settings": {
"autoplay": true,
"speed": 5,
"titleColor": "#ffffff"
},
"blocks": [
{
"block": "slide",
"name": "Slide 1",
"settings": {
"title": "Welcome to Our Store",
"buttonText": "Shop Now",
"buttonUrl": "/collections"
}
},
{
"block": "slide",
"name": "Slide 2",
"settings": {
"title": "New Arrivals",
"buttonText": "Discover",
"buttonUrl": "/collections/new"
}
}
]
}
]
}
Local Media Files
When using local media files, prefix with qumra://:
{
"image": "qumra://banner.png",
"video": "qumra://promo.mp4"
}
Quick Tips
- Use descriptive widget names (
hero-slider,product-grid,testimonials) - Define all settings in
schema.jsonand use them in the template - Use blocks for repeatable content (slides, reviews, FAQs)
- Provide sensible default values for all settings
View All Field Types & Properties →