Storefront JS
Guppy follows the standard Shopware storefront JS pattern: PluginBase classes with a lifecycle, registration with PluginManager, selector-based activation via data-* attributes. This page covers architecture, bundled plugins, configuration, and how to write your own.
Plugin architecture
Registration
// New plugins
PluginManager.register('SplideSliderPlugin', SplideSliderPlugin);
PluginManager.register('UspBannerPlugin', UspBannerPlugin);
PluginManager.register('ProductBoxClickPlugin', ProductBoxClickPlugin);
// Plugin overrides (lazy imports possible)
PluginManager.override('QuantitySelector', () => import('./js/quantity-selector.plugin'));Initialisation via data-* attributes
<div data-splide-slider-plugin="true"
data-splide-slider-plugin-options='{...}'>Bundled plugins
SplideSliderPlugin
Modern slider based on Splide.js: replaces Shopware's default Tiny Slider.
Features: better performance, full keyboard navigation, screen-reader support, touch gestures, responsive.
export default class SplideSliderPlugin extends Plugin {
static options = {
splideSnippets: ""
}
init() {
this._mountSlider(this.el, this.options.splideSnippets);
}
_mountSlider(element, options) {
var splideSlider = new Splide(element, {
i18n: options
}).mount();
const splideOptions = Object.getPrototypeOf(splideSlider.options);
if (splideOptions.autoplay === "pause") {
splideSlider.Components.Autoplay.play();
}
}
}Usage:
<div class="splide" data-splide-slider-plugin="true">
<div class="splide__track">
<ul class="splide__list">
<li class="splide__slide">Slide 1</li>
<li class="splide__slide">Slide 2</li>
</ul>
</div>
</div>UspBannerPlugin
Interactive USP banner with tooltip support and responsive behaviour.
Features: tooltip system, responsive display, slider integration on mobile, configurable layouts (default and benefits).
{
"guppy-usp-active": true,
"guppy-usp-layout": "benefits",
"guppy-usp-layout-benefit1": "Free shipping",
"guppy-usp-layout-benefit2": "30-day returns",
"guppy-usp-layout-benefit3": "Fast delivery"
}Responsive behaviour:
| Viewport | USP count |
|---|---|
| Desktop | 1–4 static |
| Tablet | 1–2 as a slider with infinite loop |
| Mobile | 1 as a slider with infinite loop |
ProductBoxClickPlugin
Extends the product box with fully clickable cards plus keyboard support.
Features: full-box click target, keyboard support, correct focus management, event delegation.
CustomCheckoutPlugin
Improves the checkout experience (validation, progress tracking, auto-save, error handling).
<div class="checkout-main" data-custom-checkout-plugin="true">QuantitySelectorPlugin (override)
Extends Shopware's quantity selector: input validation, arrow-key navigation, ARIA labels, smooth animations.
Other plugins
| Plugin | Purpose |
|---|---|
| RemoveExtraH1Plugin | SEO, removes redundant H1 tags after DOM load. |
| DeliveryInformationMarginPlugin | Dynamic margin adjustment for delivery information. |
| SplideSliderGalleryPlugin | Extended gallery with thumbnail sync and zoom. |
| CollapseFooterColumnsPlugin (override) | Accordion footer on mobile. |
Splide configuration
All Splide.js options are available.
<div class="splide"
data-splide-slider-plugin="true"
data-splide-slider-plugin-options='{
"type": "loop",
"perPage": 4,
"perMove": 1,
"gap": "1rem",
"pagination": false,
"arrows": true,
"autoplay": true,
"interval": 5000,
"pauseOnHover": true,
"breakpoints": {
"1024": { "perPage": 3 },
"768": { "perPage": 2 },
"576": { "perPage": 1 }
}
}'>| Option | Type | Default | Description |
|---|---|---|---|
type | string | slide | slide, loop, fade |
perPage | number | 1 | Visible slides |
perMove | number | 1 | Slides per move |
gap | string | 0 | Gap between slides |
arrows | boolean | true | Navigation arrows |
pagination | boolean | true | Pagination dots |
autoplay | boolean | false | Auto play |
interval | number | 5000 | Autoplay interval (ms) |
pauseOnHover | boolean | true | Pause on hover |
rewind | boolean | false | Rewind to start |
speed | number | 400 | Animation speed (ms) |
easing | string | cubic-bezier(0.25, 1, 0.5, 1) | Easing function |
Events
document.addEventListener('DOMContentLoaded', () => {
const splideEl = document.querySelector('.splide');
const splide = splideEl._splide;
if (splide) {
splide.on('move', (newIndex, prevIndex) => {
console.log(`Moved from ${prevIndex} to ${newIndex}`);
});
splide.on('mounted', () => console.log('Slider mounted'));
splide.on('autoplay:play', () => console.log('Autoplay started'));
splide.on('autoplay:pause', () => console.log('Autoplay paused'));
}
});ProductBoxClickPlugin
export default class ProductBoxClickPlugin extends Plugin {
static options = {
excludeSelectors: '.btn, .product-action, .wishlist-button, input, select',
linkSelector: '.product-name a, .product-image-link'
}
init() {
this.el.addEventListener('click', this._onClick.bind(this));
this.el.addEventListener('keydown', this._onKeydown.bind(this));
}
_onClick(event) {
if (event.target.closest(this.options.excludeSelectors)) {
return;
}
const link = this.el.querySelector(this.options.linkSelector);
if (link) {
link.click();
}
}
_onKeydown(event) {
if (event.key === 'Enter' || event.key === ' ') {
this._onClick(event);
}
}
}Write your own plugin
Plugin template
import Plugin from 'src/plugin-system/plugin.class';
export default class MyCustomPlugin extends Plugin {
static options = {
myOption: 'default-value',
anotherOption: true
};
init() {
this._registerEvents();
}
_registerEvents() {
this.el.addEventListener('click', this._onClick.bind(this));
}
_onClick(event) {
console.log('Clicked!', this.options.myOption);
}
destroy() {
// Cleanup
}
}Register the plugin
// main.js
import MyCustomPlugin from './plugins/my-custom-plugin';
window.PluginManager.register('MyCustomPlugin', MyCustomPlugin, '[data-my-custom-plugin]');Use the plugin
<div data-my-custom-plugin="true"
data-my-custom-plugin-options='{"myOption": "custom-value"}'>
<!-- content -->
</div>Debugging
// Get a plugin instance
const element = document.querySelector('[data-splide-slider-plugin]');
const pluginInstance = window.PluginManager.getPluginInstanceFromElement(element, 'SplideSliderPlugin');
// Inspect options
console.log(pluginInstance.options);
// All registered plugins
console.log(window.PluginManager.getPluginList());Common issues
| Problem | Cause | Fix |
|---|---|---|
| Plugin doesn't load | JS error | Check the browser console |
| Slider shows no slides | wrong HTML structure | Verify Splide classes |
| Events don't fire | plugin not initialised | wait for DOMContentLoaded |
| Autoplay doesn't work | autoplay: "pause" set | change to true |
Related
- Architecture: plugin structure and build output (
dist/storefront/js/). - Twig Overrides: set
data-*attributes correctly from templates. - DmfSplideSlider: shared slider component consumed by many custom elements.