Skip to content
On this page

Shopping Experiences

This guide will discuss how to use and customize Shopping Experiences in your Shopware Frontends project.

How it works

Shopping Experiences are implemented as a dedicated package that you can install in your project.

If your project is based on the Demo Store Template, that package is already installed. If you are using a custom template, follow the instructions in content pages first.

Customize Components

Only for cms-base package

The directory structure is only relevant, if you want to customize the components of the cms-base package. If you are using a custom template, you can place components where you want, because you handle their resolution by yourself. In that case skip to Create Elements.

In order to customize a component, you need to override it. The process is the same regardless what type of component you want to override

  • Sections
  • Blocks
  • Elements

To do so, you need to create a file with the same name as the component in the components/cms directory.

json
demo-store/
├─ components/
|  ├─ cms/
|  ├─ ├─ CmsBlockImageHighlightRow.vue

Now the CMS module will automatically resolve that file based on the name and you can start writing your component.

Create Elements

Start with importing the correct element type from the composables-next package and using it in the defineProps method to define the type of your content property:

vue
<!-- components/cms/CmsElementImage.vue -->

<script setup lang="ts">
import { CmsElementImage } from "@shopware-pwa/composables-next";

const props = defineProps<{
  content: CmsElementImage;
}>();
</script>

Now, you can use props.content to access all properties of the element in your template.

vue
<!-- components/cms/CmsElementImage.vue -->

<script setup lang="ts">
// see above
</script>

<template>
    <img :src="props.content.data.media.url" />
</template>

However, for some elements the configuration can be quite complex, so there are composables to give you a hand:

vue
<!-- components/cms/CmsElementImage.vue -->

<script setup lang="ts">
import { CmsElementImage, useCmsElementImage } from "@shopware-pwa/composables-next";

const props = defineProps<{
    content: CmsElementImage
}>();

const {
    containerStyle, // padding, background-color etc.
    displayMode, // cover, contain, stretch etc.
    imageAttrs, // automatically resolves src, alt and srcset attributes
} = useCmsElementImage(props.content);
</script>

<template>
    <div :style="containerStyle">
        <img v-bind="imageAttrs"/>
    </div>
</template>

Create Blocks

Make sure, you've created a new file as described in the introduction.

Next, import the correct type for your block and use it to define the content property:

vue
<!-- components/cms/CmsBlockImageThreeColumn.vue -->
<script setup lang="ts">
import { CmsBlockImageThreeColumn } from "@shopware-pwa/composables-next";

const props = defineProps<{
  content: CmsBlockImageThreeColumn;
}>();
</script>

Slots

Only for cms-base package

Also here, if you are not using the cms-base package, you have to come up with your own implementation of a generic component that handles the slot resolution. In that case, please ignore the mentions of CmsGenericElement.

Since blocks are usually layouts, they have slots which can be filled with dynamic content - CMS elements. Since blocks are flexible, the specific type of the element is not known in advance.

For that reason, there's a generic element CmsGenericElement which can be placed in every slot. It receives the content configuration as its only prop.

Let's build the image-three-column block, which has three slots - left, center and right.

vue
<!-- components/cms/CmsBlockImageThreeColumn.vue -->
<template>
    <div class="grid grid-cols-3">
        <CmsGenericElement
            :content="props.content.slots.filter(
                (slot) => slot.slot === 'left')
            " />
        <CmsGenericElement
            :content="props.content.slots.filter(
                (slot) => slot.slot === 'center')
            " />
        <CmsGenericElement
            :content="props.content.slots.filter(
                (slot) => slot.slot === 'right')
            " />
    </div>
</template>

That works, but it's quite repetiive and hard to read. So we can use another composable useCmsBlock which makes our lives way easier.

vue
<script setup lang="ts">
import { CmsBlockImageThreeColumn } from "@shopware-pwa/composables-next";

const props = defineProps<{
  content: CmsBlockImageThreeColumn;
}>();

const { getSlotContent } = useCmsBlock(props.content);

const leftContent = getSlotContent("left");
const rightContent = getSlotContent("right");
const centerContent = getSlotContent("center");
</script>
<template>
    <div class="grid grid-cols-3">
        <CmsGenericElement :content="leftContent" />
        <CmsGenericElement :content="centerContent" />
        <CmsGenericElement :content="rightContent" />
    </div>
</template>

No you can go ahead and override blocks and elements step by step.

Shopping Experiences has loaded