From b07c18bd22bb907d48120ee480d5773341c2d0e6 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 10 Mar 2026 10:52:56 +0100 Subject: [PATCH 01/11] Add documentation for Storefront Components --- concepts/framework/storefront-components.md | 505 ++++++++++++++++++ .../apps/storefront/customize-templates.md | 5 + .../plugins/storefront/customize-templates.md | 5 + guides/plugins/themes/theme-configuration.md | 83 ++- 4 files changed, 583 insertions(+), 15 deletions(-) create mode 100644 concepts/framework/storefront-components.md diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md new file mode 100644 index 0000000000..d459b0ac94 --- /dev/null +++ b/concepts/framework/storefront-components.md @@ -0,0 +1,505 @@ +--- +nav: + title: Storefront Components + position: 90 + +--- + +# Storefront Components +Since Shopware 6.7.9.0, the default Storefront includes a new component system based on [Symfony UX Twig Components](https://symfony.com/bundles/ux-twig-component/current/index.html). +It enables developers to build reusable, atomic component templates in Twig and introduces additional Shopware-specific features for handling SCSS and JavaScript, bringing a more modern, framework-like development experience to the Storefront. + +## Creating Components +Creating a new component is very simple. All you need to do is create the corresponding files for your component in the right directory. All components live in `views/components/` of their specific Symfony bundle, like the Shopware Storefront, or your own extension. There are two different ways to define a component, which will be covered in the following: + +### 1. Anonymous Components +The easiest way to define a new component is via a single template file, which is the common way for our Storefront Components. These are called anonymous components and all information for your component can directly be defined in the Twig template file. + +**Example Structure:** +``` +MyExtension/ + src/ + Resources/ + views/ + commponents/ + Button/ + Primary.html.twig +``` + +The directory and file structure of your component also defines the name of your component. Components from Shopware extensions are also automatically namespaced with the name of the extension (bundle). The shown example will create the component `MyExtension:Button:Primary`. + +In anonymous components you can define properties for your component right within the template. Properties are configuration options that can be used to pass data to your component. You can define default values for these properties and use the data within your component template as usual Twig variables. + +**Component Template:** +```Twig +{# components/Button/Primary.html.twig #} + +{% props + label = 'Click here!', + size = 'md', +%} + + +``` + +Your component can then be used in any other template by using the component name. This can be done via a specific Twig call or by using the new HTML syntax of Twig components. + +**Component Usage:** +```Twig +{# Any other template file #} + +
+ + + +
+``` + +This is just a very basic example of a component and there are a lot of more features available for Twig components. Please refer to the [official documentation](https://symfony.com/bundles/ux-twig-component/current/index.html) for all details. + +### 2. PHP Class +The second, more advanced way for creating a component is by PHP class. In Shopware we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the epxerience of a real comopnent system and you have all component related files in one place. Therefore you can simply add the PHP class to the described directory structure. + +**Note:** As this method requires a PHP file, it is only available for Shopware Plugins, but not for Apps. If you want to create components in your App, use anonymous components instead. + +**Example Structure:** +``` +MyPlugin/ + src/ + Resources/ + views/ + commponents/ + Button/ + Primary.html.twig + Primary.php + +``` + +The loading and template matching is already solved by placing the file in the right directory, so you don't have to define a specific name or template path in your component class. + +**Component Class:** +```PHP + +``` + +When the script is loaded, Shopware will automatically initialize the component class on all elements matching the selector. This also applies to elements that might be added later. You do not need to do this manually. Shopware will observe the DOM tree and initialize components also on elements that are dynamically added to the document. + +### Component Configuration +Components can be configured through a data attribute named `data-component-options`. For example, you can pass information form Twig into your component. The options should be passed as a JSON string. + +```Twig +{% set componentOptions = { + foo: "bar" + test: true +} %} + +
+
+``` + +The passed options are merged with the default options that you define as static properties in your component class. + +If you want to have an even more component-style approach, you can simply pass through the Twig component properties to your JavaScript component. + +```Twig +{# views/components/MyComponent.html.twig #} + +{% props + foo = "bar", + custom = true, +%} + +
+ + {# Some component logic ... #} +
+``` + +### Event System +To react to actions from other components, there is a new central event system available which can be accessed via the global `window.Shopware` singleton. + +In your component you can emit events to inform others about an action and pass additional data via the event. + +```javascript +// MyComponent.js + +export default class MyComponent extends ShopwareComponent { + + // ... + + doSomething() { + const message = 'Hello World!'; + + window.Shopware.emit('MyComponent:DoSomething', message); + } +} +``` + +Other components can then subscribe to this event to react to that. + +```javascript +// SomeOtherComponent.js + +export default class SomeOtherComponent extends ShopwareComponent { + + init() { + window.Shopware.on('MyComponent:DoSomething', (message) => { + this.el.innerText = message; + }); + } +} +``` + +Of course, you can also register to events from anywhere else, also from outside of the component system. For example, if you just want to extend the logic of an existing component. + +### Event Interception +In addition to the normal asynchronous events, there is a seprate event type which expects a return value that gets further processed within the component. These events make it even easier to extend a components logic and offers a bunch of different use cases, like manipulating request data before it gets send. + +For example the BuyButton component offers an event `BuyButton:PreSubmit` which is interceptable, as it is called via `emitInterception()`. It is triggered when a user clicks the buy button of a product. + +```javascript +// BuyButton.js + +export default class BuyButton extends ShopwareComponent { + + // ... + + onFormSubmit(event) { + event.preventDefault(); + + let requestUrl = this.el.getAttribute('action'); + let formData = window.Shopware.serializeForm(this.el); + + ({ requestUrl, formData } = window.Shopware.emitInterception('BuyButton:PreSubmit', { requestUrl, formData })); + + window.Shopware.emit('BuyButton:Submit', requestUrl, formData); + + window.Shopware.callPluginMethod('OffCanvasCart', 'openOffCanvas', requestUrl, formData); + } +} +``` + +You can see that the event `BuyButton:PreSubmit` offers the opportunity to manipulate the `formData` before it gets sent. From any other script you can intercept this event and work with the arguments send via the event. + +```javascript +// Intercept the buy button event +window.Shopware.intercept('BuyButton:PreSubmit', (data) => { + + data.formData.append('foo', 'bar'); + + return data; +}); +``` + +Don't forget to return the data again, so the component logic can work with it. + +There can be multiple subscribers to a single event. They will all be executed in the order as they are registered. You can change the order by passing a priority parameter as an optional third option, when registering an event. By default all subscribers have the priority `0`. The higher the priority the earlier the subscriber is called in the chain. Also negative values are possible to move a subscriber further down the chain. + +```javascript +// Another interceptor to the buy button event +window.Shopware.intercept('BuyButton:PreSubmit', (data) => { + + data.formData.delete('foo'); + data.formData.append('bar', 'baz') + + return data; +}, -10); +``` + +### Method Calling + +Besides the event system you can also access other component instances directly, or call methods for all active instances of a component. + +```javascript +// Call a method on all instances of a component +Shopware.callMethod('MyComponentNamespace:MyComponent', 'refresh'); + +// Get all instances of a component +const instances = Shopware.getComponentInstances('MyComponentNamespace:MyComponent'); + +// Get a specific instance by element +const instance = Shopware.getComponentInstanceByElement('MyComponentNamespace:MyComponent', element); +``` + +### Mutation Observation + +Components can observe DOM and attribute changes on their elements and children. The component base class offers an optional mutation observer that can be started separately if needed. + +You can call `initializeObserver()` in your component to start the observer and pass the desired observer configuration. If you want to use this, there are two additional lifecycle methods available to react to content and attribute changes. + +```javascript +class ReactiveComponent extends ShopwareComponent { + init() { + // Enable observation for content and attribute changes + this.initializeObserver({ + childList: true, + attributes: true, + subtree: true + }); + } + + onContentUpdate(mutationRecord) { + // Handle content changes + this.refreshContent(); + } + + onAttributeUpdate(mutationRecord) { + // Handle attribute changes + this.updateFromAttributes(); + } +} +``` + +## Component Documentation (Experimental) +There is support for a component library based on Storybook. This feature is still experimental and will be improved in the future. If you want to provide component documentation for the library, you can place a story definition in your component directory. + +**Example Structure:** +``` +MyExtension/ + src/ + Resources/ + views/ + commponents/ + Button/ + Primary.html.twig + Primary.scss + Primary.js + Primary.stories.json +``` + +Within the stories file you can add the Storybook configuration for your component. + +**Example Story:** +```JSON +{ + "title": "MyExtension/Button/Primary", + "parameters": { + "server": { + "id": "MyExtension:Button:Primary" + }, + "template": "" + }, + "argTypes": { + "size": { + "control": "select", + "options": ["md", "lg"], + "description": "The size of the button." + }, + "label": { + "control": "text", + "description": "The button label." + }, + }, + "stories": [ + { + "name": "Primary", + "args": { + "size": "md", + "label": "Click me!" + } + } + ] +} +``` + +The component library can be started in your local Shopware development environment with the following command. + +``` +composer storefront:storybook +``` + +**Note:** Because the component preview in the documentation requires a controller to render the Twig template it is only available in local development setups and not in production environments. \ No newline at end of file diff --git a/guides/plugins/apps/storefront/customize-templates.md b/guides/plugins/apps/storefront/customize-templates.md index f34cecb25a..0da49b4713 100644 --- a/guides/plugins/apps/storefront/customize-templates.md +++ b/guides/plugins/apps/storefront/customize-templates.md @@ -97,3 +97,8 @@ This `dump()` call will print out all variables available on this page. ::: info Once again, the plugin called [FroshDevelopmentHelper](https://github.com/FriendsOfShopware/FroshDevelopmentHelper) adds all available page data to the Twig tab in the profiler, when opening a request and its details. This might help here as well. ::: + +## Storefront Components +Since Shopware 6.7.9.0, a new component system is available which offers the possibility to build atomic template components which can be reused in a composable way throughout the Storefront. + +To learn more about how to use Storefront Components in your app, refer to the main documentation of [Storefront Components](../../../../concepts/framework/storefront-components). \ No newline at end of file diff --git a/guides/plugins/plugins/storefront/customize-templates.md b/guides/plugins/plugins/storefront/customize-templates.md index 2ac81ed1b2..f5fc6ce179 100644 --- a/guides/plugins/plugins/storefront/customize-templates.md +++ b/guides/plugins/plugins/storefront/customize-templates.md @@ -111,6 +111,11 @@ This `dump()` call will print out all variables available on this page. Once again, the plugin called [FroshDevelopmentHelper](https://github.com/FriendsOfShopware/FroshDevelopmentHelper) adds all available page data to the Twig tab in the profiler, when opening a request and its details. This might help here as well. ::: +## Storefront Components +Since Shopware 6.7.9.0, a new component system is available which offers the possibility to build atomic template components which can be reused in a composable way throughout the Storefront. + +To learn more about how to use Storefront Components in your plugin, refer to the main documentation of [Storefront Components](../../../../concepts/framework/storefront-components). + ## Next steps You are able to customize templates now, which is a good start. diff --git a/guides/plugins/themes/theme-configuration.md b/guides/plugins/themes/theme-configuration.md index a3c9600801..c00aaf9078 100644 --- a/guides/plugins/themes/theme-configuration.md +++ b/guides/plugins/themes/theme-configuration.md @@ -64,7 +64,7 @@ The theme configuration for a theme is located in the `theme.json` file `/src/Resources/theme.json @@ -81,6 +81,19 @@ Let's have a closer look at each section. Here change the `name` of your theme and the `author`. It is recommended to choose a name in camel case. The `description` section is optional and as you notice it is also translatable. +The `previewMedia` field provides a path `app/storefront/dist/assets/defaultThemePreview.jpg` to an image file that is relative to the root directory of the theme. It serves as a visual preview of the theme. This preview image is typically displayed within the Shopware administration interface or theme marketplace as a thumbnail or preview of the theme's appearance to give users an idea of how the theme will appear on their storefront before they activate it. + +```javascript +// /src/Resources/theme.json +{ + ... + "previewMedia": "app/storefront/dist/assets/defaultThemePreview.jpg", + ... +} +``` + +## Views + The `views` section controls the template inheritance. This will be covered in the [Theme inheritance](add-theme-inheritance) guide. ```javascript @@ -96,18 +109,24 @@ The `views` section controls the template inheritance. This will be covered in t } ``` -The `previewMedia` field provides a path `app/storefront/dist/assets/defaultThemePreview.jpg` to an image file that is relative to the root directory of the theme. It serves as a visual preview of the theme. This preview image is typically displayed within the Shopware administration interface or theme marketplace as a thumbnail or preview of the theme's appearance to give users an idea of how the theme will appear on their storefront before they activate it. +## Styles + +The `style` section determines the order of the CSS compilation. In the `/app/storefront/src/scss/base.scss` file you can apply your changes you want to make to the `@Storefront` standard styles or add other styles you need. The `/app/storefront/src/scss/overrides.scss` file is used for a special case. Maybe you need to override some defined `variables` or `functions` defined by Shopware or Bootstrap, you can implement your changes here. Checkout the [Override bootstrap variables in a theme](override-bootstrap-variables-in-a-theme) guide for further information. ```javascript // /src/Resources/theme.json { ... - "previewMedia": "app/storefront/dist/assets/defaultThemePreview.jpg", + "style": [ + "app/storefront/src/scss/overrides.scss", + "@Storefront", + "app/storefront/src/scss/base.scss" + ], ... } ``` -The `style` section determines the order of the CSS compilation. In the `/app/storefront/src/scss/base.scss` file you can apply your changes you want to make to the `@Storefront` standard styles or add other styles you need. The `/app/storefront/src/scss/overrides.scss` file is used for a special case. Maybe you need to override some defined `variables` or `functions` defined by Shopware or Bootstrap, you can implement your changes here. Checkout the [Override bootstrap variables in a theme](override-bootstrap-variables-in-a-theme) guide for further information. +You can add a full reference to other namespaces, like the `@Storefront` alias for the default Storefront, or other themes by referencing their technical name, like `@BasicTheme`. If you don't want to import all files from the whole namespace, you can also reference single files from a specific namespace. With this method you can also change the order in which these files are imported. ```javascript // /src/Resources/theme.json @@ -116,12 +135,46 @@ The `style` section determines the order of the CSS compilation. In the `/src/Resources/theme.json +{ + ... + "script": [ + "@Storefront", + "@Plugins", + "app/storefront/dist/storefront/js/storefront-test-app-theme/storefront-test-app-theme.js" + ], + ... +} +``` + +Also for scripts, the namespace alias and single file reference can be used. + +```javascript +// /src/Resources/theme.json +{ + ... + "script": [ + "@Storefront", + "@Plugins", + "@BasicTheme/app/storefront/dist/storefront/custom-plugin.js", + "app/storefront/dist/storefront/js/storefront-test-app-theme/storefront-test-app-theme.js" + ], + ... +} +``` + ## Assets The `asset` option you can configure your paths to your assets like images, fonts, etc. The standard location to put your assets to is the `/app/storefront/src/assets` folder. Checkout the [Add assets to theme](add-assets-to-theme) guide for further information. @@ -158,7 +211,7 @@ One of the benefits of creating a theme is that you can overwrite the theme conf ```javascript // /src/Resources/theme.json { - ... + ... "asset":[ ... ], @@ -210,8 +263,8 @@ This results in the following key structure: - **Tab**: `sw-theme...label` - **Block**: `sw-theme....label` - **Section**: `sw-theme.....label` -- **Field**: - - `sw-theme......label` +- **Field**: + - `sw-theme......label` - `sw-theme......helpText` - **Option**: `sw-theme.......label` @@ -257,12 +310,12 @@ This would generate the following snippet keys: - **Tab**: `sw-theme.justAnotherTheme.default.label` - **Block**: `sw-theme.justAnotherTheme.default.exampleBlock.label` - **Section**: `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.label` -- **Field**: - - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.label` +- **Field**: + - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.label` - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.helpText` -- **Option**: - - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.0.label` - - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.1.label` +- **Option**: + - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.0.label` + - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.1.label` - `sw-theme.justAnotherTheme.default.exampleBlock.exampleSection.my-single-select-field.2.label` ## Field types @@ -917,7 +970,7 @@ You can extend the config to add translated labels for the tabs, blocks and sect "en-GB": "Colours", "de-DE": "Farben" } - } + } }, "fields": { "sw-color-brand-primary": { @@ -942,14 +995,14 @@ You can extend the config to add translated labels for the tabs, blocks and sect ## Config inheritance -The `configInheritance` option lets you configure additional themes from which your theme will inherit its fields configuration and snippets. Every theme will always inherit the fields from the `Storefront` standard theme. With this option you can add additional other themes. For example, you can have a basic theme for your corporate design and special themes for different sales channels with specific changes only needed for a single sales channel. +The `configInheritance` option lets you configure additional themes from which your theme will inherit its fields configuration and snippets. Every theme will always inherit the fields from the `Storefront` standard theme. With this option you can add additional other themes. For example, you can have a basic theme for your corporate design and special themes for different sales channels with specific changes only needed for a single sales channel. ```javascript // /src/Resources/theme.json { ... "configInheritance": [ - "@Storefront", + "@Storefront", "@BasicTheme" ] ... From d8fdfba6b1843a252e7c73197676badc9ca35d09 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 11 Mar 2026 07:18:06 +0100 Subject: [PATCH 02/11] Implement review feedback --- concepts/framework/storefront-components.md | 99 +++++++++++++++------ 1 file changed, 73 insertions(+), 26 deletions(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index d459b0ac94..da9f67e9e5 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -28,6 +28,21 @@ MyExtension/ The directory and file structure of your component also defines the name of your component. Components from Shopware extensions are also automatically namespaced with the name of the extension (bundle). The shown example will create the component `MyExtension:Button:Primary`. +There is also the option to name the template file `index.html.twig` to just use the directory name as the component name. This can be usefull if you have a larger namespace with several sub-components, or you just want to avoid the nesting but still keep all files of your component in one place. + +**Example Structure:** +``` +MyExtension/ + src/ + Resources/ + views/ + commponents/ + Button/ + index.html.twig +``` + +This example will create the component `MyExtension:Button`. + In anonymous components you can define properties for your component right within the template. Properties are configuration options that can be used to pass data to your component. You can define default values for these properties and use the data within your component template as usual Twig variables. **Component Template:** @@ -64,7 +79,7 @@ This is just a very basic example of a component and there are a lot of more fea ### 2. PHP Class The second, more advanced way for creating a component is by PHP class. In Shopware we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the epxerience of a real comopnent system and you have all component related files in one place. Therefore you can simply add the PHP class to the described directory structure. -**Note:** As this method requires a PHP file, it is only available for Shopware Plugins, but not for Apps. If you want to create components in your App, use anonymous components instead. +**Note:** As this method requires a PHP file, it is only available for [Shopware Plugins](https://developer.shopware.com/docs/guides/plugins/#at-a-glance), but not for Apps. If you want to create components in your App, use anonymous components instead. **Example Structure:** ``` @@ -235,12 +250,14 @@ Also, JavaScript files of components are only made available if the theme refere Inside your component script file you export a new class that extends the central `ShopwareComponent` class, which is globally available. The name of the component class does not have to follow a particular pattern, but the name of the script file should have the same name as your Twig component and should be located right beside the template file. ```javascript -export default class MyComponent extends ShopwareComponent { +// components/Button/Primary.js + +export default class ButtonPrimary extends ShopwareComponent { // Define default options static options = { - foo: 'bar', - test: false + label: 'Click me!', + size: 'md' }; // Component initialization logic @@ -271,7 +288,20 @@ Components don't have to be registered manually. If the script file of your comp Shopware generates an importmap for all components based on the Twig component tag name. On initialization, Shopware will search for all elements with a `data-component` attribute and will try to load the corresponding script file, if necessary. Just make sure to add the data attribute, including the tag name of your Twig component, to the root element of your component. ```Twig -
+{# components/Button/Primary.html.twig #} + +{% props + label = 'Click here!', + size = 'md', +%} + + ``` When the script is loaded, Shopware will automatically initialize the component class on all elements matching the selector. This also applies to elements that might be added later. You do not need to do this manually. Shopware will observe the DOM tree and initialize components also on elements that are dynamically added to the document. @@ -280,14 +310,26 @@ When the script is loaded, Shopware will automatically initialize the component Components can be configured through a data attribute named `data-component-options`. For example, you can pass information form Twig into your component. The options should be passed as a JSON string. ```Twig +{# components/Button/Primary.html.twig #} + +{% props + label = 'Click here!', + size = 'md', +%} + {% set componentOptions = { - foo: "bar" - test: true + size: size } %} -
-
+ ``` The passed options are merged with the default options that you define as static properties in your component class. @@ -295,18 +337,22 @@ The passed options are merged with the default options that you define as static If you want to have an even more component-style approach, you can simply pass through the Twig component properties to your JavaScript component. ```Twig -{# views/components/MyComponent.html.twig #} +{# components/Button/Primary.html.twig #} {% props - foo = "bar", - custom = true, + label = 'Click here!', + size = 'md', %} -
+
+ {% block content %} + {{ label }} + {% endblock %} + + ``` ### Event System @@ -315,16 +361,16 @@ To react to actions from other components, there is a new central event system a In your component you can emit events to inform others about an action and pass additional data via the event. ```javascript -// MyComponent.js +// components/Button/Primary.js -export default class MyComponent extends ShopwareComponent { +export default class ButtonPrimary extends ShopwareComponent { // ... doSomething() { const message = 'Hello World!'; - window.Shopware.emit('MyComponent:DoSomething', message); + window.Shopware.emit('ButtomPrimary:DoSomething', message); } } ``` @@ -332,12 +378,12 @@ export default class MyComponent extends ShopwareComponent { Other components can then subscribe to this event to react to that. ```javascript -// SomeOtherComponent.js +// components/Some/Other/Component.js export default class SomeOtherComponent extends ShopwareComponent { init() { - window.Shopware.on('MyComponent:DoSomething', (message) => { + window.Shopware.on('ButtomPrimary:DoSomething', (message) => { this.el.innerText = message; }); } @@ -406,13 +452,13 @@ Besides the event system you can also access other component instances directly, ```javascript // Call a method on all instances of a component -Shopware.callMethod('MyComponentNamespace:MyComponent', 'refresh'); +Shopware.callMethod('MyExtension:Button:Primary', 'doSomething'); // Get all instances of a component -const instances = Shopware.getComponentInstances('MyComponentNamespace:MyComponent'); +const instances = Shopware.getComponentInstances('MyExtension:Button:Primary'); // Get a specific instance by element -const instance = Shopware.getComponentInstanceByElement('MyComponentNamespace:MyComponent', element); +const instance = Shopware.getComponentInstanceByElement('MyExtension:Button:Primary', element); ``` ### Mutation Observation @@ -422,7 +468,8 @@ Components can observe DOM and attribute changes on their elements and children. You can call `initializeObserver()` in your component to start the observer and pass the desired observer configuration. If you want to use this, there are two additional lifecycle methods available to react to content and attribute changes. ```javascript -class ReactiveComponent extends ShopwareComponent { +class ButtonPrimary extends ShopwareComponent { + init() { // Enable observation for content and attribute changes this.initializeObserver({ From 04194805007c45f6ef1d9d7a57c842b982d8c478 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 11 Mar 2026 07:36:09 +0100 Subject: [PATCH 03/11] Fix markdown style --- concepts/framework/storefront-components.md | 66 +++++++++++++++------ 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index da9f67e9e5..281a7d4f39 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -6,17 +6,21 @@ nav: --- # Storefront Components + Since Shopware 6.7.9.0, the default Storefront includes a new component system based on [Symfony UX Twig Components](https://symfony.com/bundles/ux-twig-component/current/index.html). It enables developers to build reusable, atomic component templates in Twig and introduces additional Shopware-specific features for handling SCSS and JavaScript, bringing a more modern, framework-like development experience to the Storefront. ## Creating Components + Creating a new component is very simple. All you need to do is create the corresponding files for your component in the right directory. All components live in `views/components/` of their specific Symfony bundle, like the Shopware Storefront, or your own extension. There are two different ways to define a component, which will be covered in the following: ### 1. Anonymous Components + The easiest way to define a new component is via a single template file, which is the common way for our Storefront Components. These are called anonymous components and all information for your component can directly be defined in the Twig template file. **Example Structure:** -``` + +```Plaintext MyExtension/ src/ Resources/ @@ -31,7 +35,8 @@ The directory and file structure of your component also defines the name of your There is also the option to name the template file `index.html.twig` to just use the directory name as the component name. This can be usefull if you have a larger namespace with several sub-components, or you just want to avoid the nesting but still keep all files of your component in one place. **Example Structure:** -``` + +```Plaintext MyExtension/ src/ Resources/ @@ -46,6 +51,7 @@ This example will create the component `MyExtension:Button`. In anonymous components you can define properties for your component right within the template. Properties are configuration options that can be used to pass data to your component. You can define default values for these properties and use the data within your component template as usual Twig variables. **Component Template:** + ```Twig {# components/Button/Primary.html.twig #} @@ -64,6 +70,7 @@ In anonymous components you can define properties for your component right withi Your component can then be used in any other template by using the component name. This can be done via a specific Twig call or by using the new HTML syntax of Twig components. **Component Usage:** + ```Twig {# Any other template file #} @@ -77,12 +84,14 @@ Your component can then be used in any other template by using the component nam This is just a very basic example of a component and there are a lot of more features available for Twig components. Please refer to the [official documentation](https://symfony.com/bundles/ux-twig-component/current/index.html) for all details. ### 2. PHP Class + The second, more advanced way for creating a component is by PHP class. In Shopware we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the epxerience of a real comopnent system and you have all component related files in one place. Therefore you can simply add the PHP class to the described directory structure. -**Note:** As this method requires a PHP file, it is only available for [Shopware Plugins](https://developer.shopware.com/docs/guides/plugins/#at-a-glance), but not for Apps. If you want to create components in your App, use anonymous components instead. +**Note:** As this method requires a PHP file, it is only available for [Shopware Plugins](../../guides/plugins/index.md), but not for Apps. If you want to create components in your App, use anonymous components instead. **Example Structure:** -``` + +```Plaintext MyPlugin/ src/ Resources/ @@ -97,6 +106,7 @@ MyPlugin/ The loading and template matching is already solved by placing the file in the right directory, so you don't have to define a specific name or template path in your component class. **Component Class:** + ```PHP { @@ -435,7 +462,7 @@ Don't forget to return the data again, so the component logic can work with it. There can be multiple subscribers to a single event. They will all be executed in the order as they are registered. You can change the order by passing a priority parameter as an optional third option, when registering an event. By default all subscribers have the priority `0`. The higher the priority the earlier the subscriber is called in the chain. Also negative values are possible to move a subscriber further down the chain. -```javascript +```JavaScript // Another interceptor to the buy button event window.Shopware.intercept('BuyButton:PreSubmit', (data) => { @@ -450,7 +477,7 @@ window.Shopware.intercept('BuyButton:PreSubmit', (data) => { Besides the event system you can also access other component instances directly, or call methods for all active instances of a component. -```javascript +```JavaScript // Call a method on all instances of a component Shopware.callMethod('MyExtension:Button:Primary', 'doSomething'); @@ -467,7 +494,7 @@ Components can observe DOM and attribute changes on their elements and children. You can call `initializeObserver()` in your component to start the observer and pass the desired observer configuration. If you want to use this, there are two additional lifecycle methods available to react to content and attribute changes. -```javascript +```JavaScript class ButtonPrimary extends ShopwareComponent { init() { @@ -492,10 +519,12 @@ class ButtonPrimary extends ShopwareComponent { ``` ## Component Documentation (Experimental) + There is support for a component library based on Storybook. This feature is still experimental and will be improved in the future. If you want to provide component documentation for the library, you can place a story definition in your component directory. **Example Structure:** -``` + +```Plaintext MyExtension/ src/ Resources/ @@ -511,6 +540,7 @@ MyExtension/ Within the stories file you can add the Storybook configuration for your component. **Example Story:** + ```JSON { "title": "MyExtension/Button/Primary", @@ -545,8 +575,8 @@ Within the stories file you can add the Storybook configuration for your compone The component library can be started in your local Shopware development environment with the following command. -``` +```Bash composer storefront:storybook ``` -**Note:** Because the component preview in the documentation requires a controller to render the Twig template it is only available in local development setups and not in production environments. \ No newline at end of file +**Note:** Because the component preview in the documentation requires a controller to render the Twig template it is only available in local development setups and not in production environments. From bb83a62eaeea921769e66e2fcf575dad60e2d236 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 11 Mar 2026 07:37:47 +0100 Subject: [PATCH 04/11] Fix markdown style --- guides/plugins/apps/storefront/customize-templates.md | 3 ++- guides/plugins/plugins/storefront/customize-templates.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/guides/plugins/apps/storefront/customize-templates.md b/guides/plugins/apps/storefront/customize-templates.md index 0da49b4713..967ed5ba93 100644 --- a/guides/plugins/apps/storefront/customize-templates.md +++ b/guides/plugins/apps/storefront/customize-templates.md @@ -99,6 +99,7 @@ Once again, the plugin called [FroshDevelopmentHelper](https://github.com/Friend ::: ## Storefront Components + Since Shopware 6.7.9.0, a new component system is available which offers the possibility to build atomic template components which can be reused in a composable way throughout the Storefront. -To learn more about how to use Storefront Components in your app, refer to the main documentation of [Storefront Components](../../../../concepts/framework/storefront-components). \ No newline at end of file +To learn more about how to use Storefront Components in your app, refer to the main documentation of [Storefront Components](../../../../concepts/framework/storefront-components). diff --git a/guides/plugins/plugins/storefront/customize-templates.md b/guides/plugins/plugins/storefront/customize-templates.md index f5fc6ce179..f6b6df50ec 100644 --- a/guides/plugins/plugins/storefront/customize-templates.md +++ b/guides/plugins/plugins/storefront/customize-templates.md @@ -112,6 +112,7 @@ Once again, the plugin called [FroshDevelopmentHelper](https://github.com/Friend ::: ## Storefront Components + Since Shopware 6.7.9.0, a new component system is available which offers the possibility to build atomic template components which can be reused in a composable way throughout the Storefront. To learn more about how to use Storefront Components in your plugin, refer to the main documentation of [Storefront Components](../../../../concepts/framework/storefront-components). From f8d6aa75b28c598a1adec882aef5945f233ba47a Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 11 Mar 2026 07:58:00 +0100 Subject: [PATCH 05/11] Fix spelling issues --- concepts/framework/storefront-components.md | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index 281a7d4f39..b246b7b36f 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -32,7 +32,7 @@ MyExtension/ The directory and file structure of your component also defines the name of your component. Components from Shopware extensions are also automatically namespaced with the name of the extension (bundle). The shown example will create the component `MyExtension:Button:Primary`. -There is also the option to name the template file `index.html.twig` to just use the directory name as the component name. This can be usefull if you have a larger namespace with several sub-components, or you just want to avoid the nesting but still keep all files of your component in one place. +There is also the option to name the template file `index.html.twig` to just use the directory name as the component name. This can be useful if you have a larger namespace with several sub-components, or you just want to avoid the nesting but still keep all files of your component in one place. **Example Structure:** @@ -85,7 +85,7 @@ This is just a very basic example of a component and there are a lot of more fea ### 2. PHP Class -The second, more advanced way for creating a component is by PHP class. In Shopware we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the epxerience of a real comopnent system and you have all component related files in one place. Therefore you can simply add the PHP class to the described directory structure. +The second, more advanced way for creating a component is by PHP class. In Shopware we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the experience of a real component system, and you have all component-related files in one place. Therefore you can simply add the PHP class to the described directory structure. **Note:** As this method requires a PHP file, it is only available for [Shopware Plugins](../../guides/plugins/index.md), but not for Apps. If you want to create components in your App, use anonymous components instead. @@ -188,7 +188,7 @@ If you don't want to compile all component styles in your theme, you can referen } ``` -With this method you can add only specific component syles in a defined order to the compiled styles of your theme. As there might be components from different bundles with the same naming pattern, you can also explicitly reference a component from a specific namespace. +With this method, you can add only specific component styles in a defined order to the compiled styles of your theme. As there might be components from different bundles with the same naming pattern, you can also explicitly reference a component from a specific namespace. **Example Configuration:** @@ -206,21 +206,21 @@ With this method you can add only specific component syles in a defined order to ## Adding Component JavaScript -For Twig components that have to implement interactive funcationality via JavaScript, we introduce a corresponding JavaScript component system, which can be seen as the successor of the former JS plugin system. There are some parts which will seem familiar if you aleady know the plugin system, but some parts were changed and improved. +For Twig components that need to implement interactive functionality via JavaScript, we introduce a corresponding JavaScript component system that can be seen as the successor to the former JS plugin system. Some parts will seem familiar if you already know the plugin system, but others have been changed and improved. ### Major differences between plugin and component system 1. **Automatic initialization** - If the component is implemented properly it will automatically be initialized on the corresponding elements. Even if the DOM tree changes and elements are added or removed, the component will automatically be initiallized on added elements or destroyed for removed elements. No more manual re-initialization of plugins that have to work in conjunction after dynamic DOM changes. + If the component is implemented properly it will automatically be initialized on the corresponding elements. Even if the DOM tree changes and elements are added or removed, the component will automatically be initialized on added elements or destroyed for removed elements. No more manual re-initialization of plugins that have to work in conjunction after dynamic DOM changes. 2. **No registration needed** - The component system uses native ES module loading that does everyhting for you, if you follow the conventions. The script will automatically be loaded and initialized on corresponding elements just based on the component's name. + The component system uses native ES module loading that does everything for you, if you follow the conventions. The script will automatically be loaded and initialized on corresponding elements just based on the component's name. 3. **Better events instead of overrides** - The current override technique of the plugin system was not reintroduced to the component system, as it showed some major flaws, as overrides could only happen once which can lead to conflicts between different Shopware exntensions. Instead there is a central event system which is easier to use and offers a more robust public interface. In addtion, it offers special interception events, for example, to manupilate request data before it is send. + The current override technique of the plugin system was not reintroduced to the component system because it had major flaws: overrides could occur only once, which can lead to conflicts between different Shopware extensions. Instead, there is a central event system which is easier to use and offers a more robust public interface. In addition, it offers special interception events, for example, to manipulate request data before it is sent. 4. **No imports** - We decided to make everything related to the component system available via global scope. This means it is available at the `window` object level and can directly be used in plain JavaScript. No imports or bundling is necessary. You can still use the bundling as it is avialable today or use your own build processes if desired, but the component scripts target for plain JavaScript that don't need to be build in conjuction with our core files. + We decided to make everything related to the component system available via global scope. This means it is available at the `window` object level and can directly be used in plain JavaScript. No imports or bundling are necessary. You can still use the bundling as it is available today or use your own build processes if desired, but the component scripts target plain JavaScript that don't need to be built in conjunction with our core files. ### Component Script Files @@ -309,7 +309,7 @@ export default class ButtonPrimary extends ShopwareComponent { Components don't have to be registered manually. If the script file of your component follows the rules of the Twig component directory structure, they are automatically loaded via ES module loading. -Shopware generates an importmap for all components based on the Twig component tag name. On initialization, Shopware will search for all elements with a `data-component` attribute and will try to load the corresponding script file, if necessary. Just make sure to add the data attribute, including the tag name of your Twig component, to the root element of your component. +Shopware generates an import map for all components based on the Twig component tag name. On initialization, Shopware will search for all elements with a `data-component` attribute and will try to load the corresponding script file, if necessary. Just make sure to add the data attribute, including the tag name of your Twig component, to the root element of your component. ```Twig {# components/Button/Primary.html.twig #} @@ -420,9 +420,9 @@ Of course, you can also register to events from anywhere else, also from outside ### Event Interception -In addition to the normal asynchronous events, there is a seprate event type which expects a return value that gets further processed within the component. These events make it even easier to extend a components logic and offers a bunch of different use cases, like manipulating request data before it gets send. +In addition to the normal asynchronous events, there is a separate event type that expects a return value that gets further processed within the component. These events make it even easier to extend a component's logic and offer a bunch of different use cases, like manipulating request data before it gets sent. -For example the BuyButton component offers an event `BuyButton:PreSubmit` which is interceptable, as it is called via `emitInterception()`. It is triggered when a user clicks the buy button of a product. +For example, the BuyButton component offers an event `BuyButton:PreSubmit`, which is interceptable because it is called via `emitInterception()`. It is triggered when a user clicks the buy button of a product. ```JavaScript // BuyButton.js From d0a0a749d4b1eb1a08d7b4b187296f3fd9dbc49e Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 20 Mar 2026 09:59:12 +0100 Subject: [PATCH 06/11] Add code review feedback --- concepts/framework/storefront-components.md | 92 ++++++++++++++++++-- guides/plugins/themes/theme-configuration.md | 2 +- 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index b246b7b36f..55a7016c2a 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -18,7 +18,7 @@ Creating a new component is very simple. All you need to do is create the corres The easiest way to define a new component is via a single template file, which is the common way for our Storefront Components. These are called anonymous components and all information for your component can directly be defined in the Twig template file. -**Example Structure:** +**Example Structure for Plugins:** ```Plaintext MyExtension/ @@ -30,11 +30,22 @@ MyExtension/ Primary.html.twig ``` +**Example Structure for Apps:** + +```Plaintext +MyExtension/ + Resources/ + views/ + commponents/ + Button/ + Primary.html.twig +``` + The directory and file structure of your component also defines the name of your component. Components from Shopware extensions are also automatically namespaced with the name of the extension (bundle). The shown example will create the component `MyExtension:Button:Primary`. There is also the option to name the template file `index.html.twig` to just use the directory name as the component name. This can be useful if you have a larger namespace with several sub-components, or you just want to avoid the nesting but still keep all files of your component in one place. -**Example Structure:** +**Example Structure for Plugins:** ```Plaintext MyExtension/ @@ -46,6 +57,17 @@ MyExtension/ index.html.twig ``` +**Example Structure for Apps:** + +```Plaintext +MyExtension/ + Resources/ + views/ + commponents/ + Button/ + index.html.twig +``` + This example will create the component `MyExtension:Button`. In anonymous components you can define properties for your component right within the template. Properties are configuration options that can be used to pass data to your component. You can define default values for these properties and use the data within your component template as usual Twig variables. @@ -89,7 +111,7 @@ The second, more advanced way for creating a component is by PHP class. In Shopw **Note:** As this method requires a PHP file, it is only available for [Shopware Plugins](../../guides/plugins/index.md), but not for Apps. If you want to create components in your App, use anonymous components instead. -**Example Structure:** +**Example Structure for Plugins:** ```Plaintext MyPlugin/ @@ -125,6 +147,21 @@ class Primary } ``` +The component class must be registered as a service in your plugin's `services.xml` with `autoconfigure="true"`. This lets Symfony read the `#[AsTwigComponent]` attribute and wire up everything — including public property exposure — automatically. Without this registration the PHP class is unknown to the container and Twig silently falls back to an anonymous (template-only) component. + +```XML + + + + + + + + +``` + This creates the same component as the example in the anonymous components section, but here the component's properties are defined in the PHP class as public attributes. To learn what kind of possibilities the PHP implementation of your component offers, you can just refer to the [official documentation](https://symfony.com/bundles/ux-twig-component/current/index.html). @@ -133,7 +170,7 @@ To learn what kind of possibilities the PHP implementation of your component off By default, there is no corresponding style system in Twig components, but we wanted to provide a seamless component system, like other modern frontend frameworks. Therefore, we added automated style handling to the Storefront Components, which works similarly to other theme styles in Shopware. All you need to do is create a matching SCSS file for your component, which follows the same naming pattern. -**Example Structure:** +**Example Structure for Plugins:** ```Plaintext MyExtension/ @@ -146,6 +183,18 @@ MyExtension/ Primary.scss ``` +**Example Structure for Apps:** + +```Plaintext +MyExtension/ + Resources/ + views/ + commponents/ + Button/ + Primary.html.twig + Primary.scss +``` + Within that SCSS file, you can add your component-specific styles and also access the default SCSS variables of Shopware. For the future we have ideas on how to load component styles more dynamically based on their usage. For now, component styles are not compiled automatically. They must be referenced by a theme to be added to the compiled `all.css` file. This can be done in two different ways. @@ -206,9 +255,9 @@ With this method, you can add only specific component styles in a defined order ## Adding Component JavaScript -For Twig components that need to implement interactive functionality via JavaScript, we introduce a corresponding JavaScript component system that can be seen as the successor to the former JS plugin system. Some parts will seem familiar if you already know the plugin system, but others have been changed and improved. +For Twig components that need to implement interactive functionality via JavaScript, we introduce a corresponding JavaScript component system that can be seen as the successor to the former JS plugin system. Some parts will seem familiar if you already known the JS plugin system, but others have been changed and improved. -### Major differences between plugin and component system +### Major differences between JS plugin and component system 1. **Automatic initialization** If the component is implemented properly it will automatically be initialized on the corresponding elements. Even if the DOM tree changes and elements are added or removed, the component will automatically be initialized on added elements or destroyed for removed elements. No more manual re-initialization of plugins that have to work in conjunction after dynamic DOM changes. @@ -226,7 +275,7 @@ For Twig components that need to implement interactive functionality via JavaScr Similar to other component files, you can place the JavaScript file in the directory of your component. -**Example Structure:** +**Example Structure for Plugins:** ```Plaintext MyExtension/ @@ -240,6 +289,19 @@ MyExtension/ Primary.js ``` +**Example Structure for Apps:** + +```Plaintext +MyExtension/ + Resources/ + views/ + commponents/ + Button/ + Primary.html.twig + Primary.scss + Primary.js +``` + Also, JavaScript files of components are only made available if the theme references the file in the script configuration. Other then the style files, the script files are not compiled into the main script file of the theme, but are separately loaded via **native ES module loading**. Shopware creates an import map for all referenced component script files and the corresponding files are loaded dynamically, only if the component is rendered on the actual page. But for component script files to be added to the import map, they need to be referenced in the theme script configuration. Again, the global alias or single-file references are possible. **Global Component Script Reference:** @@ -522,7 +584,7 @@ class ButtonPrimary extends ShopwareComponent { There is support for a component library based on Storybook. This feature is still experimental and will be improved in the future. If you want to provide component documentation for the library, you can place a story definition in your component directory. -**Example Structure:** +**Example Structure for Plugins:** ```Plaintext MyExtension/ @@ -537,6 +599,20 @@ MyExtension/ Primary.stories.json ``` +**Example Structure for Apps:** + +```Plaintext +MyExtension/ + Resources/ + views/ + commponents/ + Button/ + Primary.html.twig + Primary.scss + Primary.js + Primary.stories.json +``` + Within the stories file you can add the Storybook configuration for your component. **Example Story:** diff --git a/guides/plugins/themes/theme-configuration.md b/guides/plugins/themes/theme-configuration.md index c00aaf9078..b561c6d4f9 100644 --- a/guides/plugins/themes/theme-configuration.md +++ b/guides/plugins/themes/theme-configuration.md @@ -111,7 +111,7 @@ The `views` section controls the template inheritance. This will be covered in t ## Styles -The `style` section determines the order of the CSS compilation. In the `/app/storefront/src/scss/base.scss` file you can apply your changes you want to make to the `@Storefront` standard styles or add other styles you need. The `/app/storefront/src/scss/overrides.scss` file is used for a special case. Maybe you need to override some defined `variables` or `functions` defined by Shopware or Bootstrap, you can implement your changes here. Checkout the [Override bootstrap variables in a theme](override-bootstrap-variables-in-a-theme) guide for further information. +The `style` section determines the order of the CSS compilation. In the `/src/Resources/app/storefront/src/scss/base.scss` file you can apply your changes you want to make to the `@Storefront` standard styles or add other styles you need. The `/src/Resources/app/storefront/src/scss/overrides.scss` file is used for a special case. Maybe you need to override some defined `variables` or `functions` defined by Shopware or Bootstrap, you can implement your changes here. Checkout the [Override bootstrap variables in a theme](override-bootstrap-variables-in-a-theme) guide for further information. ```javascript // /src/Resources/theme.json From 7a69c0cbc433a4af8e0960664dc1bd4812bfd54a Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 20 Mar 2026 10:31:43 +0100 Subject: [PATCH 07/11] Fix spelling --- concepts/framework/storefront-components.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index 55a7016c2a..9eb2b6fc99 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -107,7 +107,7 @@ This is just a very basic example of a component and there are a lot of more fea ### 2. PHP Class -The second, more advanced way for creating a component is by PHP class. In Shopware we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the experience of a real component system, and you have all component-related files in one place. Therefore you can simply add the PHP class to the described directory structure. +The second, more advanced way for creating a component is by PHP class. In Shopware, we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the experience of a real component system, and you have all component-related files in one place. Therefore, you can simply add the PHP class to the described directory structure. **Note:** As this method requires a PHP file, it is only available for [Shopware Plugins](../../guides/plugins/index.md), but not for Apps. If you want to create components in your App, use anonymous components instead. @@ -255,7 +255,7 @@ With this method, you can add only specific component styles in a defined order ## Adding Component JavaScript -For Twig components that need to implement interactive functionality via JavaScript, we introduce a corresponding JavaScript component system that can be seen as the successor to the former JS plugin system. Some parts will seem familiar if you already known the JS plugin system, but others have been changed and improved. +For Twig components that need to implement interactive functionality via JavaScript, we introduce a corresponding JavaScript component system that can be seen as the successor to the former JS plugin system. Some parts will seem familiar if you already know the JS plugin system, but others have been changed and improved. ### Major differences between JS plugin and component system @@ -302,7 +302,7 @@ MyExtension/ Primary.js ``` -Also, JavaScript files of components are only made available if the theme references the file in the script configuration. Other then the style files, the script files are not compiled into the main script file of the theme, but are separately loaded via **native ES module loading**. Shopware creates an import map for all referenced component script files and the corresponding files are loaded dynamically, only if the component is rendered on the actual page. But for component script files to be added to the import map, they need to be referenced in the theme script configuration. Again, the global alias or single-file references are possible. +Also, JavaScript files of components are only made available if the theme references the file in the script configuration. Other than the style files, the script files are not compiled into the main script file of the theme, but are separately loaded via **native ES module loading**. Shopware creates an import map for all referenced component script files and the corresponding files are loaded dynamically, only if the component is rendered on the actual page. But for component script files to be added to the import map, they need to be referenced in the theme script configuration. Again, the global alias or single-file references are possible. **Global Component Script Reference:** @@ -522,7 +522,7 @@ window.Shopware.intercept('BuyButton:PreSubmit', (data) => { Don't forget to return the data again, so the component logic can work with it. -There can be multiple subscribers to a single event. They will all be executed in the order as they are registered. You can change the order by passing a priority parameter as an optional third option, when registering an event. By default all subscribers have the priority `0`. The higher the priority the earlier the subscriber is called in the chain. Also negative values are possible to move a subscriber further down the chain. +There can be multiple subscribers to a single event. They will all be executed in the order as they are registered. You can change the order by passing a priority parameter as an optional third option, when registering an event. By default, all subscribers have the priority `0`. The higher the priority the earlier the subscriber is called in the chain. Also negative values are possible to move a subscriber further down the chain. ```JavaScript // Another interceptor to the buy button event From 0034df5bd3c4ea38d3aecee9c717f6c1db8aea2e Mon Sep 17 00:00:00 2001 From: Micha Date: Fri, 20 Mar 2026 15:11:42 +0100 Subject: [PATCH 08/11] fix/comma-and-grammar --- concepts/framework/storefront-components.md | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index 9eb2b6fc99..b7086aeeb1 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -12,11 +12,11 @@ It enables developers to build reusable, atomic component templates in Twig and ## Creating Components -Creating a new component is very simple. All you need to do is create the corresponding files for your component in the right directory. All components live in `views/components/` of their specific Symfony bundle, like the Shopware Storefront, or your own extension. There are two different ways to define a component, which will be covered in the following: +Creating a new component is simple. All you need to do is create the corresponding files for your component in the right directory. All components live in `views/components/` of their specific Symfony bundle, like the Shopware Storefront, or your own extension. There are two different ways to define a component, which will be covered in the following: ### 1. Anonymous Components -The easiest way to define a new component is via a single template file, which is the common way for our Storefront Components. These are called anonymous components and all information for your component can directly be defined in the Twig template file. +The easiest way to define a new component is via a single template file, which is the common way for our Storefront Components. These are called anonymous components, and all information for your component can directly be defined in the Twig template file. **Example Structure for Plugins:** @@ -43,7 +43,7 @@ MyExtension/ The directory and file structure of your component also defines the name of your component. Components from Shopware extensions are also automatically namespaced with the name of the extension (bundle). The shown example will create the component `MyExtension:Button:Primary`. -There is also the option to name the template file `index.html.twig` to just use the directory name as the component name. This can be useful if you have a larger namespace with several sub-components, or you just want to avoid the nesting but still keep all files of your component in one place. +There is also the option to name the template file `index.html.twig` to just use the directory name as the component name. This can be useful if you have a larger namespace with several subcomponents, or you just want to avoid the nesting but still keep all files of your component in one place. **Example Structure for Plugins:** @@ -103,11 +103,11 @@ Your component can then be used in any other template by using the component nam ``` -This is just a very basic example of a component and there are a lot of more features available for Twig components. Please refer to the [official documentation](https://symfony.com/bundles/ux-twig-component/current/index.html) for all details. +This is just a very basic example of a component, and there are a lot of more features available for Twig components. Please refer to the [official documentation](https://symfony.com/bundles/ux-twig-component/current/index.html) for all details. ### 2. PHP Class -The second, more advanced way for creating a component is by PHP class. In Shopware, we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the experience of a real component system, and you have all component-related files in one place. Therefore, you can simply add the PHP class to the described directory structure. +The second, more advanced way for creating a component is by PHP class. In Shopware, we decided that these PHP classes should be placed right where your component template and other files of your component are located. This provides the experience of a real component system, and you have all component-related files in one place. Therefore, you can add the PHP class to the described directory structure. **Note:** As this method requires a PHP file, it is only available for [Shopware Plugins](../../guides/plugins/index.md), but not for Apps. If you want to create components in your App, use anonymous components instead. @@ -147,7 +147,7 @@ class Primary } ``` -The component class must be registered as a service in your plugin's `services.xml` with `autoconfigure="true"`. This lets Symfony read the `#[AsTwigComponent]` attribute and wire up everything — including public property exposure — automatically. Without this registration the PHP class is unknown to the container and Twig silently falls back to an anonymous (template-only) component. +The component class must be registered as a service in your plugin's `services.xml` with `autoconfigure="true"`. This lets Symfony read the `#[AsTwigComponent]` attribute and wire up everything — including public property exposure — automatically. Without this registration the PHP class is unknown to the container, and Twig silently falls back to an anonymous (template-only) component. ```XML @@ -302,7 +302,7 @@ MyExtension/ Primary.js ``` -Also, JavaScript files of components are only made available if the theme references the file in the script configuration. Other than the style files, the script files are not compiled into the main script file of the theme, but are separately loaded via **native ES module loading**. Shopware creates an import map for all referenced component script files and the corresponding files are loaded dynamically, only if the component is rendered on the actual page. But for component script files to be added to the import map, they need to be referenced in the theme script configuration. Again, the global alias or single-file references are possible. +Also, JavaScript files of components are only made available if the theme references the file in the script configuration. Other than the style files, the script files are not compiled into the main script file of the theme but are separately loaded via **native ES module loading**. Shopware creates an import map for all referenced component script files, and the corresponding files are loaded dynamically, only if the component is rendered on the actual page. But for component script files to be added to the import map, they need to be referenced in the theme script configuration. Again, the global alias or single-file references are possible. **Global Component Script Reference:** @@ -394,7 +394,7 @@ When the script is loaded, Shopware will automatically initialize the component ### Component Configuration -Components can be configured through a data attribute named `data-component-options`. For example, you can pass information form Twig into your component. The options should be passed as a JSON string. +Components can be configured through a data attribute named `data-component-options`. For example, you can pass information from Twig into your component. The options should be passed as a JSON string. ```Twig {# components/Button/Primary.html.twig #} @@ -444,7 +444,7 @@ If you want to have an even more component-style approach, you can simply pass t ### Event System -To react to actions from other components, there is a new central event system available which can be accessed via the global `window.Shopware` singleton. +To react to actions from other components, there is a new central event system available that can be accessed via the global `window.Shopware` singleton. In your component you can emit events to inform others about an action and pass additional data via the event. @@ -478,7 +478,7 @@ export default class SomeOtherComponent extends ShopwareComponent { } ``` -Of course, you can also register to events from anywhere else, also from outside of the component system. For example, if you just want to extend the logic of an existing component. +Of course, you can also register events from anywhere else, also from outside the component system. For example, if you just want to extend the logic of an existing component. ### Event Interception @@ -508,7 +508,7 @@ export default class BuyButton extends ShopwareComponent { } ``` -You can see that the event `BuyButton:PreSubmit` offers the opportunity to manipulate the `formData` before it gets sent. From any other script you can intercept this event and work with the arguments send via the event. +You can see that the event `BuyButton:PreSubmit` offers the opportunity to manipulate the `formData` before it gets sent. From any other script you can intercept this event and work with the arguments sent via the event. ```JavaScript // Intercept the buy button event @@ -522,7 +522,7 @@ window.Shopware.intercept('BuyButton:PreSubmit', (data) => { Don't forget to return the data again, so the component logic can work with it. -There can be multiple subscribers to a single event. They will all be executed in the order as they are registered. You can change the order by passing a priority parameter as an optional third option, when registering an event. By default, all subscribers have the priority `0`. The higher the priority the earlier the subscriber is called in the chain. Also negative values are possible to move a subscriber further down the chain. +There can be multiple subscribers to a single event. They will all be executed in the order as they are registered. You can change the order by passing a priority parameter as an optional third option when registering an event. By default, all subscribers have the priority `0`. The higher the priority, the earlier the subscriber is called in the chain. Also, negative values are possible to move a subscriber further down the chain. ```JavaScript // Another interceptor to the buy button event @@ -537,7 +537,7 @@ window.Shopware.intercept('BuyButton:PreSubmit', (data) => { ### Method Calling -Besides the event system you can also access other component instances directly, or call methods for all active instances of a component. +Besides the event system, you can also access other component instances directly or call methods for all active instances of a component. ```JavaScript // Call a method on all instances of a component @@ -613,7 +613,7 @@ MyExtension/ Primary.stories.json ``` -Within the stories file you can add the Storybook configuration for your component. +Within the `stories` file you can add the Storybook configuration for your component. **Example Story:** @@ -655,4 +655,4 @@ The component library can be started in your local Shopware development environm composer storefront:storybook ``` -**Note:** Because the component preview in the documentation requires a controller to render the Twig template it is only available in local development setups and not in production environments. +**Note:** Because the component preview in the documentation requires a controller to render the Twig template, it is only available in local development setups and not in production environments. From 400aaa923e31c42bdcb8c5d6949617a65f13cb74 Mon Sep 17 00:00:00 2001 From: Micha Date: Fri, 20 Mar 2026 15:12:30 +0100 Subject: [PATCH 09/11] fix/spellcheck --- .wordlist.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.wordlist.txt b/.wordlist.txt index ecf69353c9..b80b5d5c10 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -122,6 +122,7 @@ BreadcrumbField Browserlist BusinessEvent BusinessEventCollector +BuyButton CDN CDNs CHANGELOG @@ -1533,6 +1534,7 @@ initializers installable instantiation integrations +interceptable intl invalidations io @@ -1650,6 +1652,7 @@ myPluginLogHandler mysql mysqldump namespace +namespaced namespaces natively nav From a1acc5d5e08e741c31afeeaee3ef3ded6a5ec29a Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 23 Mar 2026 14:27:11 +0100 Subject: [PATCH 10/11] Change to PHP style service configuration --- concepts/framework/storefront-components.md | 29 ++++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index b7086aeeb1..a50184c5fb 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -147,19 +147,22 @@ class Primary } ``` -The component class must be registered as a service in your plugin's `services.xml` with `autoconfigure="true"`. This lets Symfony read the `#[AsTwigComponent]` attribute and wire up everything — including public property exposure — automatically. Without this registration the PHP class is unknown to the container, and Twig silently falls back to an anonymous (template-only) component. - -```XML - - - - - - - - +The component class must be registered as a service in your plugin's service configuration with `autoconfigure`. This lets Symfony read the `#[AsTwigComponent]` attribute and wire up everything — including public property exposure — automatically. Without this registration the PHP class is unknown to the container, and Twig silently falls back to an anonymous (template-only) component. + +```PHP +// src/Resources/config/services.php +services(); + + $services->set(Primary::class) + ->autoconfigure(true); +}; + ``` This creates the same component as the example in the anonymous components section, but here the component's properties are defined in the PHP class as public attributes. From d0c7b5b26053a6e58e3fd51035035ebc31daeb60 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 23 Mar 2026 18:14:35 +0100 Subject: [PATCH 11/11] Fix code example --- concepts/framework/storefront-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/framework/storefront-components.md b/concepts/framework/storefront-components.md index a50184c5fb..5e7354f5f4 100644 --- a/concepts/framework/storefront-components.md +++ b/concepts/framework/storefront-components.md @@ -506,7 +506,7 @@ export default class BuyButton extends ShopwareComponent { window.Shopware.emit('BuyButton:Submit', requestUrl, formData); - window.Shopware.callPluginMethod('OffCanvasCart', 'openOffCanvas', requestUrl, formData); + window.PluginManager.callPluginMethod('OffCanvasCart', 'openOffCanvas', requestUrl, formData); } } ```