Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Storefront Laravel Package

shopware/storefront

The Shopware Storefront package provides the customer-facing web frontend for Shopware 6, with Twig templates, theme assets, routing, and controllers for product, category, checkout, and account pages, enabling extensible responsive storefront experiences.

View on GitHub
Deep Wiki
Context7

Getting Started

shopware/storefront is the Storefront bundle of Shopware 6 — the Twig/SCSS/JavaScript layer that renders the shop frontend. It is almost never installed standalone; it arrives as a dependency of shopware/platform (or the shopware/shopware project skeleton). The "67 stars / 0 dependents" figures are misleading: real usage is via the meta-package, and the code you'll actually touch is your own plugin's src/Resources/ tree, not this bundle's source.

Minimal setup:

composer create-project shopware/production my-shop
# or, for core hacking:
git clone https://github.com/shopware/shopware.git && cd shopware
composer install && ./bin/build-storefront.sh

Then, to see the layer you'll be extending, read these paths inside the bundle first:

  • Resources/views/storefront/ — base templates (layout/, page/, component/, block/, element/). This directory is the API you extend.
  • Resources/app/storefront/src/plugin-system/ — the JS Plugin base class and PluginManager.
  • Theme/ and Resources/theme.json — theme inheritance, SCSS variable pipeline.
  • Page/ + Controller/ — the page loader that assembles PageletStruct objects into PageStruct for rendering.

First use case, in order of likelihood: override one template block and add a JS plugin. Scaffold a plugin (bin/console plugin:create MyPlugin), then:

{# src/Resources/views/storefront/block/cms-block-image-text.html.twig #}
{% sw_extends '@Storefront/storefront/block/cms-block-image-text.html.twig' %}

{% block block_image_text_text %}
    <div class="my-banner">{{ parent() }}</div>
{% endblock %}
// src/Resources/app/storefront/src/main.js
import MyPlugin from './my-plugin.plugin';
const PluginManager = window.PluginManager;
PluginManager.register('MyPlugin', MyPlugin, '[data-my-plugin]');
bin/console theme:compile
bin/console cache:clear

If the template renders but the JS doesn't, you skipped the asset build — see Gotchas.

Implementation Patterns

Template extension is block-based, not file-based. Shopware compiles a template inheritance chain across all active plugins plus the theme. sw_extends is a Shopware tag, not {% extends %} — it records your template in the chain so other plugins can extend you. Always call {{ parent() }} unless you intend to replace. Override the narrowest block you can find rather than copying a whole file; block names are stable-ish, template paths are not.

Page loading: extend the loader, not the controller. Storefront controllers are thin; the work happens in AbstractPageLoader subclasses registered via the DI container tag shopware.storefront.page_loader. To add data to a product detail page:

class MyProductPageLoader extends AbstractPageLoader
{
    public function load(Shopware\HttpFoundation\Request $request, SalesChannelContext $context): PageStruct
    {
        $page = $this->decorated->load($request, $context); // decorate, don't replace
        if ($page instanceof ProductPage) {
            $page->getProduct()->addExtension('my_extension', new ArrayStruct(['foo' => 'bar']));
        }
        return $page;
    }
}

Register as a decorator on Shopware\Storefront\Page\Product\ProductPageLoader. Extensions surface in Twig as product.extensions.my_extension.foo. This is the cleanest integration point and survives Shopware minor upgrades far better than controller overrides.

JS plugins are declarative via data attributes. PluginManager.register('Name', Class, selector) instantiates the class per matching element, mapping data-name-options='{"foo":1}' to this.options.foo and data-name="true" to the selector. Options merge from defaultOptions in the class. Communicate via this.$emitter.publish('eventName', payload) and this.$emitter.subscribe(...) rather than reaching into other plugins' internals. Use this._registerInstance / PluginManager.initializePlugins() after injecting DOM via AJAX, otherwise dynamically added elements get no plugin.

Theme config is the SCSS API. Anything you want to make swappable per sales channel goes into theme.json:

{
  "name": "MyTheme",
  "author": "me",
  "views": ["@Storefront", "@MyPlugin"],
  "style": ["src/scss/overrides.scss"],
  "config": {
    "sw-color-brand-primary": {
      "label": { "en-GB": "Primary", "de-DE": "Primär" },
      "type": "color",
      "value": "#0d6efd",
      "scss": true
    }
  }
}

With "scss": true the value lands as $sw-color-brand-primary in the SCSS compile, so overrides.scss can reference it. views controls template fallback order — put @Storefront last so your theme wins.

Snippets, not hardcoded strings. src/Resources/snippet/storefront.en-GB.json under the storefront namespace; reference with {{ 'myPlugin.label'|trans }}. Missing snippet keys fail silently in production and print the raw key in dev.

Cache and HTTP layer are part of the design. Pagelets, ESI, and sw_ cache tags mean a naive {% if app.request... %} in a shared layout block can poison cached fragments for other sales channels. If output varies by context, put it behind a pagelet or an AbstractCartSubscriber-style event, and check HttpCacheKeyGenerator behavior when debugging "wrong user's cart" reports.

Gotchas and Tips

  • Asset build is separate from PHP. theme:compile compiles SCSS/JS into public/theme/. In dev use bin/watch-storefront.sh (or ./psh.phar storefront:watch in older setups). New JS files added to Resources/app/storefront/src/ require a rebuild — a cache:clear will not pick them up.
  • sw_extends vs extends. Using plain Twig extends compiles fine and silently breaks the inheritance chain for every plugin downstream. Same for {% sw_include %} vs {% include %} when you want the include to be overridable.
  • parent() in a block that has no parent throws. When you replace a block rather than wrap it, drop the call. When you extend a template but override a block defined in a grandparent, parent() still resolves — but debugging "block not found" errors almost always means a typo in a block name that was renamed between minor versions. Diff against the bundle source after upgrades.
  • theme.json changes need theme:refresh semantics. New config fields don't appear in the admin until the theme is re-registered/compiled; a plain cache:clear leaves stale compiled CSS. If your new SCSS variable is undefined, it's a stale compile, not a PHP bug.
  • Plugin JS options are strings. data-foo-options='{"limit": 5}' gives you the number; data-foo-limit="5" gives you "5". Use the options attribute for anything non-boolean, and validate in init().
  • Selector collisions with the plugin manager. Plugin names are global; two plugins registering 'FormSubmit' means the second wins silently. Namespace your plugin names (MyVendorFormSubmit).
  • PluginManager runs before your DOM is guaranteed. For plugins that manipulate layout, use init() for setup and subscribe to PluginEvents or DOMContentLoaded-adjacent hooks for reads that need final layout. Reading offsetWidth in init() is a classic zero-value bug.
  • Don't edit the bundle in vendor/. Every change dies on composer update, and Shopware's own upgrade path assumes an unmodified vendor tree. Overrides belong in a plugin or a project theme.
  • Debug Twig without a debugger: {{ dump() }} needs TwigBundle debug enabled; more reliably, bin/console debug:twig --filter=sw_extends and bin/console debug:container --tag=shopware.storefront.page_loader tell you what's actually registered. bin/console lint:twig src/Resources/views catches the syntax errors that only surface at render time otherwise.
  • Extension points worth knowing before you hack core: StorefrontRenderEvent (last-mile template params), ThemeCompiler events for asset pipelines, AbstractPageLoader decoration, StorefrontController route defaults (@Storefront annotation sets the route scope), and the storefront snippet namespace. Most "I need to change core behavior" problems have one of these as the intended answer.
  • Version pin. This bundle's template block names and JS plugin APIs shift between 6.x minors (notably 6.4 → 6.5 → 6.6 on the JS/asset side). Pin shopware/core and shopware/storefront to the same version constraint and read the UPGRADE notes for block renames before bumping.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package