Repository: shopware/frontends
Pull request #1096 (2024-07-09 11:07:31)

feat(vue-demo-store): display more info about not handled entities
### Description

closes: #1091 

- propagates the API error to the nuxt upper levels
- adds console error with the API details
- makes the 404 error for unknown seoUrl consistent

### Type of change

<!-- Comment out the type of change your PR is making -->

<!-- Bug fix (non-breaking change that fixes an issue) -->
New feature (non-breaking change which adds functionality)
<!-- Breaking change (fix or feature that would cause existing functionality to not work as expected) -->

### ToDo's

<!-- Add the todo's that need to be done before merge -->
<!-- Remember to run `pnpm run changeset` and describe your change (plus potential migration guide/important notes) to your pull request. -->

<!-- - [ ] Changeset file provided [read more](https://github.com/shopware/frontends/blob/main/CONTRIBUTION.md#changelog-preparation) -->
<!-- - [ ] Documentation added/updated -->
<!-- - [ ] Unit-Tests added/updated -->
<!-- - [ ] E2E-Tests added/updated -->
<!-- - [ ] Related Issue updated -->

### Screenshots (if applicable)


![image](https://github.com/shopware/frontends/assets/5596960/88053a69-9290-4467-a31e-38e931c7281e)

console:
![image](https://github.com/shopware/frontends/assets/5596960/08fb025c-e437-409c-a225-0ebc991b0f2f)

### Additional context

<!-- Add any other context about the pull request here. -->


vercel[bot]: [vc]: #5HG8IPIzBfqwAVTrPRV07ixu70EMkcKpHXiQnKinYIg=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJzaG9wd2FyZS1mcm9udGVuZHMtZG9jcyIsInJvb3REaXJlY3RvcnkiOiJhcHBzL2RvY3MvIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3Nob3B3YXJlLWZyb250ZW5kcy9zaG9wd2FyZS1mcm9udGVuZHMtZG9jcy9EanNGVEVEeFBnbWI5SnBUS0UzOXgzdlVDYzlYIiwicHJldmlld1VybCI6InNob3B3YXJlLWZyb250ZW5kcy1kb2NzLWdpdC1mZWF0LWRpc3AtM2ViNDc2LXNob3B3YXJlLWZyb250ZW5kcy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn0seyJuYW1lIjoiZnJvbnRlbmRzLWRlbW8iLCJyb290RGlyZWN0b3J5IjoidGVtcGxhdGVzL3Z1ZS1kZW1vLXN0b3JlIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3Nob3B3YXJlLWZyb250ZW5kcy9mcm9udGVuZHMtZGVtby9Fd1RhUlhDQ3N4RHR2RzlONGNoZFJ4UGFvNTVaIiwicHJldmlld1VybCI6ImZyb250ZW5kcy1kZW1vLWdpdC1mZWF0LWRpc3BsYXlpbmctZXItN2UzYmY1LXNob3B3YXJlLWZyb250ZW5kcy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated (UTC) |
| :--- | :----- | :------ | :------ |
| **frontends-demo** | ✅ Ready ([Inspect](https://vercel.com/shopware-frontends/frontends-demo/EwTaRXCCsxDtvG9N4chdRxPao55Z)) | [Visit Preview](https://frontends-demo-git-feat-displaying-er-7e3bf5-shopware-frontends.vercel.app) | Jul 9, 2024 11:07am |
| **shopware-frontends-docs** | ✅ Ready ([Inspect](https://vercel.com/shopware-frontends/shopware-frontends-docs/DjsFTEDxPgmb9JpTKE39x3vUCc9X)) | [Visit Preview](https://shopware-frontends-docs-git-feat-disp-3eb476-shopware-frontends.vercel.app) | Jul 9, 2024 11:07am |



mkucmus: @kstala please check the 404 scenarios for:
- products (clearance setting for products)
- categories
- landing pages
codspeed-hq[bot]: ## [CodSpeed Performance Report](https://codspeed.io/shopware/frontends/branches/feat/displaying-errors-for-entity-resolvers)

### Merging #1096 will **not alter performance**

<sub>Comparing <code>feat/displaying-errors-for-entity-resolvers</code> (7010b79) with <code>main</code> (f9d2735)</sub>



### Summary

`✅ 9` untouched benchmarks






Patch:
diff --git a/.changeset/light-cows-protect.md b/.changeset/light-cows-protect.md
new file mode 100644
index 00000000..f4740106
--- /dev/null
+++ b/.changeset/light-cows-protect.md
@@ -0,0 +1,5 @@
+---
+"vue-demo-store": minor
+---
+
+Added a pagination for the order history page and wishlist page.
diff --git a/.changeset/tough-queens-swim.md b/.changeset/tough-queens-swim.md
new file mode 100644
index 00000000..2706eac5
--- /dev/null
+++ b/.changeset/tough-queens-swim.md
@@ -0,0 +1,9 @@
+---
+"@shopware-pwa/composables-next": minor
+---
+
+- `useWishList`:
+  - Added `getCurrentPage` and `getTotalPagesCount` to the returned object
+  - Changed `getWishlistProducts` to accept `page` and `query` as optional parameters
+- `useSyncWishList`:
+  - Changed `getWishlistProducts`, added Parameter to pass default criterias
diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts
index f6bf5a7b..f6cc17e9 100644
--- a/apps/docs/.vitepress/config.ts
+++ b/apps/docs/.vitepress/config.ts
@@ -112,7 +112,7 @@ export const sidebar = [
           },
           {
             text: "Multiple CMS",
-            link: "/getting-started/cms/multiple-cms",
+            link: "/getting-started/cms/multiple-cms.html",
           },
         ],
       },
@@ -138,10 +138,6 @@ export const sidebar = [
             text: "Payments",
             link: "/getting-started/e-commerce/payments.html",
           },
-          {
-            text: "Custom Payment",
-            link: "/getting-started/e-commerce/custom-payment.html",
-          },
           {
             text: "JSON-LD",
             link: "/getting-started/e-commerce/json-ld.html",
@@ -342,10 +338,6 @@ export default defineConfigWithTheme<ThemeConfigExtended>({
   // srcExclude: ["tutorial/**/description.md"], In case we need something to be excluded
   scrollOffset: "header",
   ignoreDeadLinks: true, // remove once MR #294 is merged
-  rewrites: {
-    "installation.md": "getting-started/templates/demo-store-template",
-    "installation.html": "getting-started/templates/demo-store-template",
-  },
   head: [
     [
       "link",
diff --git a/apps/docs/.vitepress/navigation.ts b/apps/docs/.vitepress/navigation.ts
index bf69b40d..f8178459 100644
--- a/apps/docs/.vitepress/navigation.ts
+++ b/apps/docs/.vitepress/navigation.ts
@@ -1,10 +1,10 @@
 const nav = [
   {
-    link: "https://developer.shopware.com/docs/guides/plugins/apps",
+    link: "https://developer.shopware.com/docs/guides/plugins/apps/",
     text: "Apps",
   },
   {
-    link: "https://developer.shopware.com/docs/guides/plugins/themes",
+    link: "https://developer.shopware.com/docs/guides/plugins/themes/",
     text: "Themes",
   },
   {
diff --git a/apps/docs/src/framework/internal-structure.md b/apps/docs/src/framework/internal-structure.md
index 478fbdd4..fc15efd4 100644
--- a/apps/docs/src/framework/internal-structure.md
+++ b/apps/docs/src/framework/internal-structure.md
@@ -52,7 +52,7 @@ Helpers are functions that can be used for formatting, data manipulation and oth
     <img src="../.assets/framework-icons/vue.png" alt="This package depends on Vue.js 3" title="This package depends on Vue.js 3" class="w-6 aspect-square hover:scale-125 transition hover:drop-shadow-md" /> | <a href="https://www.npmjs.com/package/@shopware-pwa/composables-next" target="_blank">@shopware-pwa/composables-next</a>
 </div>
 
-The composables are a set of Vue.js composition functions that can be used in any Vue.js project. They provide state management, UI logic and data fetching and are the base for all guides in our [building section](./../getting-started/).
+The composables are a set of Vue.js composition functions that can be used in any Vue.js project. They provide state management, UI logic and data fetching and are the base for all guides in our [building section](/getting-started/).
 
 <PageRef page="../packages/composables.html" title="Composables Reference" sub="Package API reference with all composables" />
 
@@ -66,7 +66,7 @@ The composables are a set of Vue.js composition functions that can be used in an
 
 The Nuxt 3 module allows you to set up a Nuxt 3 project with Shopware Frontends. It provides the [composables](#composables) and [api-client](#api-client) packages.
 
-If you want to use these packages with a different Vue.js framework, see the guide for using Shopware Frontends in a [custom project](../getting-started/templates/custom-vue-project.html).
+If you want to use these packages with a different Vue.js framework, see the guide for using Shopware Frontends in a [custom project](/getting-started/templates/custom-vue-project.html).
 
 <PageRef page="../packages/nuxt3-module.html" title="Nuxt3 Module Reference" sub="Documentation about setup and basic usage" />
 
@@ -81,7 +81,7 @@ If you want to use these packages with a different Vue.js framework, see the gui
 
 The CMS base is a Nuxt module that provides an implementation of all CMS components in Shopware [based on utility-classes](./styling.html) using unocss/Tailwind.css syntax. It is useful for projects that want to use the CMS components but design their own layout.
 
-Head to our [Content Pages](../getting-started/cms/content-pages.html#use-the-cms-base-package) guide to learn more.
+Head to our [Content Pages](/getting-started/cms/content-pages.html#use-the-cms-base-package) guide to learn more.
 
 <PageRef page="../packages/cms-base.html" title="CMS Base Reference" sub="Package API reference for the CMS composables" />
 
diff --git a/apps/docs/src/framework/shopping-experiences.md b/apps/docs/src/framework/shopping-experiences.md
index e8d767bc..687bf4c3 100644
--- a/apps/docs/src/framework/shopping-experiences.md
+++ b/apps/docs/src/framework/shopping-experiences.md
@@ -21,7 +21,7 @@ This guide will discuss how to use and customize [Shopping Experiences](https://
 
 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](../getting-started/templates/demo-store-template.html), that package is already installed. If you are using a custom template, follow the instructions in [Install the package](#install-the-package) first.
+If your project is based on the [Demo Store Template](/getting-started/templates/demo-store-template.html), that package is already installed. If you are using a custom template, follow the instructions in [Install the package](#install-the-package) first.
 
 ## Install the package
 
diff --git a/apps/docs/src/framework/styling.md b/apps/docs/src/framework/styling.md
index 9dcc9dca..d462a90b 100644
--- a/apps/docs/src/framework/styling.md
+++ b/apps/docs/src/framework/styling.md
@@ -15,7 +15,7 @@ nav:
 
 # Styling
 
-Shopware Frontends [Demo Store Template](../getting-started/templates/demo-store-template.html) applies a utility-first styling approach based on [unocss](https://github.com/unocss/unocss). You can either follow this approach or use [custom styling](#use-a-custom-css-framework).
+Shopware Frontends [Demo Store Template](/getting-started/templates/demo-store-template.html) applies a utility-first styling approach based on [unocss](https://github.com/unocss/unocss). You can either follow this approach or use [custom styling](#use-a-custom-css-framework).
 
 ## Utility CSS
 
@@ -25,7 +25,7 @@ Unocss supports multiple CSS frameworks, including
 - Windy CSS
 - Bootstrap
 
-This means you can use utilities like `mt-10` or `bg-gray-100` in all of your components along with styles like `col-md-3`. Note, that the [Demo Store Template](../getting-started/templates/demo-store-template.html) applies only Tailwind CSS syntax and does not mix any of the approaches.
+This means you can use utilities like `mt-10` or `bg-gray-100` in all of your components along with styles like `col-md-3`. Note, that the [Demo Store Template](/getting-started/templates/demo-store-template.html) applies only Tailwind CSS syntax and does not mix any of the approaches.
 
 Unocss will analyse your components and generate a CSS file that contains only the utility classes used in the implementation.
 
@@ -117,11 +117,11 @@ Similar to viewport breakpoints, you can also use state variants with prefixes:
 
 ## Use a custom CSS Framework
 
-If you want to use a different CSS framework or fully custom styling, it's recommended to use the [Blank Template](../getting-started/templates/blank-template.html) as a starting point. It has no pre-installed CSS framework and you can install you own.
+If you want to use a different CSS framework or fully custom styling, it's recommended to use the [Blank Template](/getting-started/templates/blank-template.html) as a starting point. It has no pre-installed CSS framework and you can install you own.
 
 ### Remove unocss from the Demo Store Template
 
-However, it's also possible to remove unocss from the [Demo Store Template](../getting-started/templates/demo-store-template.html). This might be applicable when you want to make use of the component structure and logic that's already provided by the template.
+However, it's also possible to remove unocss from the [Demo Store Template](/getting-started/templates/demo-store-template.html). This might be applicable when you want to make use of the component structure and logic that's already provided by the template.
 
 Remove the `unocss` dependency from the `package.json` file
 
diff --git a/apps/docs/src/getting-started/cms/content-pages.md b/apps/docs/src/getting-started/cms/content-pages.md
index fc6aa083..85b51382 100644
--- a/apps/docs/src/getting-started/cms/content-pages.md
+++ b/apps/docs/src/getting-started/cms/content-pages.md
@@ -15,7 +15,7 @@ nav:
 
 # Create content pages
 
-In this chapter you will learn how to display content pages with data from Shopware's own CMS. It is based on the mechanism of routing and fetching page content described in the [previous chapter](../routing.html). The case of building fully custom components from scratch will be covered as well. Specifically, you will learn how to
+In this chapter you will learn how to display content pages with data from Shopware's own CMS. It is based on the mechanism of routing and fetching page content described in the [previous chapter](/getting-started/routing.html). The case of building fully custom components from scratch will be covered as well. Specifically, you will learn how to
 
 - Render a content page using the cms-base package
 - Render a content page using custom components
@@ -27,18 +27,18 @@ Using the cms-base package, you don't have to implement any CMS components by yo
 ### Install the package
 
 Depending on which template you are using you need to install the package first.  
-See [install the package](../../framework/shopping-experiences.html#install-the-package).
+See [install the package](/framework/shopping-experiences.html#install-the-package).
 
 ### Render the page
 
-Now, you can import all components from the `@shopware-pwa/cms-base` package and use them in your templates. The most straightforward way to render a page is to use the `CmsPage` component. It takes a `content` prop and resolves all subordinate sections, blocks and elements automatically. Put the following code in your catch-all component that also handles the [routing logic](../routing.md#resolve-a-route-to-a-page).
+Now, you can import all components from the `@shopware-pwa/cms-base` package and use them in your templates. The most straightforward way to render a page is to use the `CmsPage` component. It takes a `content` prop and resolves all subordinate sections, blocks and elements automatically. Put the following code in your catch-all component that also handles the [routing logic](/getting-started/routing.html#resolve-a-route-to-a-page).
 
 ```vue-html
 <CmsPage v-if="data" :content="data.cmsPage"/>
 ```
 
 :::warning This will only work
-if you followed the previous chapter on [routing](../routing.html). The `data` value in this example is a reactive reference to either a product, a category or a landing page response. If you are not sure how to get the data otherwise, check that chapter and see how the data is fetched.
+if you followed the previous chapter on [routing](/getting-started/routing.html). The `data` value in this example is a reactive reference to either a product, a category or a landing page response. If you are not sure how to get the data otherwise, check that chapter and see how the data is fetched.
 :::
 
 ### Customize components
@@ -49,4 +49,4 @@ The `cms-base` package has an opinionated style of components and is based on Ta
 
 ## Use custom components
 
-If you use custom components and not the cms-base package, you have to ensure the correct rendering of the page. You also need to re-create all components that the Shopware CMS uses. For the creation of custom components - such as elements or blocks, you can follow the instructions given in [Shopping Experiences](../../framework/shopping-experiences.html) and benefit from typehinting and the `useCms*` composables.
+If you use custom components and not the cms-base package, you have to ensure the correct rendering of the page. You also need to re-create all components that the Shopware CMS uses. For the creation of custom components - such as elements or blocks, you can follow the instructions given in [Shopping Experiences](/framework/shopping-experiences.html) and benefit from typehinting and the `useCms*` composables.
diff --git a/apps/docs/src/getting-started/cms/index.md b/apps/docs/src/getting-started/cms/index.md
index d5338be7..ead6b52f 100644
--- a/apps/docs/src/getting-started/cms/index.md
+++ b/apps/docs/src/getting-started/cms/index.md
@@ -6,7 +6,7 @@ nav:
 
 # CMS
 
-Everything related to CMS ([Shopping Experiences](../../framework/shopping-experiences.html)).
+Everything related to CMS ([Shopping Experiences](/framework/shopping-experiences.html)).
 <PageRef page="content-pages.html" title="Create content pages" sub="In this chapter you will learn how to display content pages with data from Shopware's own CMS." />
 <PageRef page="customize-components.html" title="Customize Components" sub="In order to customize a component, you need to override it." />
 <PageRef page="create-blocks.html" title="Create Blocks" sub="In this chapter you will learn how to create CMS blocks." />
diff --git a/apps/docs/src/getting-started/cms/multiple-cms.md b/apps/docs/src/getting-started/cms/multiple-cms.md
index 034fb3f2..299992ec 100644
--- a/apps/docs/src/getting-started/cms/multiple-cms.md
+++ b/apps/docs/src/getting-started/cms/multiple-cms.md
@@ -13,7 +13,7 @@ head:
 
 # Multiple content management systems (CMS)
 
-Add another [CMS system](../../resources/integrations/index.md), but still use the Shopware Shopping Experiences as a fallback.
+Add another [CMS system](/resources/integrations/cms/), but still use the Shopware Shopping Experiences as a fallback.
 
 This documentation guides users through the process of incorporating an additional CMS instance and use it seamlessly with the Shopware Shopping Experiences.
 
@@ -57,7 +57,7 @@ const { data: seoResult } = await useAsyncData(
   "cmsResponse" + routePath,
   async () => {
     // For client links if the history state contains seo url information we can omit the api call
-    if (process.client) {
+    if (import.meta.client) {
       if (history.state?.routeName) {
         return {
           routeName: history.state?.routeName,
@@ -112,4 +112,4 @@ function render() {
 
 You see that the `cmsPageRendererComponent` is returned before the regular `cmsPageView` is resolved. But only if the `cmsPageRendererComponent` is not null and **no** routeName aka componentName is found. Further details can be found in the comments in the code above.
 
-Also, you can find a complete example here at [Strapi CMS Integration](../../resources/integrations/cms/strapi.md).
+Also, you can find a complete example here at [Strapi CMS Integration](/resources/integrations/cms/strapi.html).
diff --git a/apps/docs/src/getting-started/e-commerce/prices.md b/apps/docs/src/getting-started/e-commerce/prices.md
index 4c865772..fec5b57f 100644
--- a/apps/docs/src/getting-started/e-commerce/prices.md
+++ b/apps/docs/src/getting-started/e-commerce/prices.md
@@ -293,7 +293,7 @@ const defaultPrice = computed(() => {
 
 ## useProductPrice composable
 
-See dedicated [Composables > useProductPrice](../../packages/composables.html#useproductprice) page to check the details of the helper function.
+See dedicated [Composables > useProductPrice](/packages/composables.html#useproductprice) page to check the details of the helper function.
 
 ### Product listing
 
diff --git a/apps/docs/src/getting-started/features/custom-products.md b/apps/docs/src/getting-started/features/custom-products.md
index dad64922..bd74f3eb 100644
--- a/apps/docs/src/getting-started/features/custom-products.md
+++ b/apps/docs/src/getting-started/features/custom-products.md
@@ -36,7 +36,7 @@ The composable is a main place to keep the logic related to _custom product_ fea
 :::warning
 Works only if the `useProduct` is fulfilled and the product data is known. Typically on Product Details Page, when the product context is provided.
 
-Visit the [useProduct](../../packages/composables.html#useproduct) reference to see more details.
+Visit the [useProduct]/packages/composables.html#useproduct) reference to see more details.
 :::
 
 ```ts
diff --git a/apps/docs/src/getting-started/page-elements/breadcrumbs.md b/apps/docs/src/getting-started/page-elements/breadcrumbs.md
index fb898bb8..75bcb0fa 100644
--- a/apps/docs/src/getting-started/page-elements/breadcrumbs.md
+++ b/apps/docs/src/getting-started/page-elements/breadcrumbs.md
@@ -22,8 +22,8 @@ In this chapter you will learn how to
 
 ### Quick reference
 
-- [useBreadcrumbs](../../packages/composables.html#usebreadcrumbs) is a composable used for a breadcrumbs management with sharable state
-- [getCategoryBreadcrumbs](../../packages/helpers.html#getcategorybreadcrumbs) is a helper used for converting `Category` to the `Breadcrumb` object
+- [useBreadcrumbs](/packages/composables.html#usebreadcrumbs) is a composable used for a breadcrumbs management with sharable state
+- [getCategoryBreadcrumbs](/packages/helpers.html#getcategorybreadcrumbs) is a helper used for converting `Category` to the `Breadcrumb` object
 
 ## Building breadcrumbs for a static page
 
@@ -49,7 +49,7 @@ Each CMS page contains the `Category` with `breadcrumb` array, which contains a
 breadcrumb: ["Home", "Main navigation ", "Summer Party"]
 ```
 
-we can convert current `string` array to the `Breadcrumb` object using [getCategoryBreadcrumbs](../../packages/helpers.html#getcategorybreadcrumbs) helper, and then pass it to [useBreadcrumbs](../../packages/composables.html#usebreadcrumbs) composable.
+we can convert current `string` array to the `Breadcrumb` object using [getCategoryBreadcrumbs](/packages/helpers.html#getcategorybreadcrumbs) helper, and then pass it to [useBreadcrumbs](/packages/composables.html#usebreadcrumbs) composable.
 
 ```ts
 import { getCategoryBreadcrumbs } from "@shopware-pwa/helpers-next";
@@ -74,7 +74,7 @@ onBeforeRouteLeave(() => {
 
 ## Displaying Breadcrumbs
 
-Breadcrumbs are stored in sharable variable `breadcrumbs` in [useBreadcrumbs](../../packages/composables.html#usebreadcrumbs) composable.
+Breadcrumbs are stored in sharable variable `breadcrumbs` in [useBreadcrumbs](/packages/composables.html#usebreadcrumbs) composable.
 
 ```vue
 <script setup lang="ts">
diff --git a/apps/docs/src/getting-started/page-elements/examples/footer-navigation/index.md b/apps/docs/src/getting-started/page-elements/examples/footer-navigation/index.md
index ca347fb3..16ddef63 100644
--- a/apps/docs/src/getting-started/page-elements/examples/footer-navigation/index.md
+++ b/apps/docs/src/getting-started/page-elements/examples/footer-navigation/index.md
@@ -10,7 +10,7 @@ Just copy the code snippet and paste it into your project. Sometimes it's useful
 
 Implementing Footer navigation can be described in few steps:
 
-1. Use [useNavigation](../../../../packages/composables.html#usenavigation) composable to `loadNavigationElements` to display a navigation configured in admin panel.
+1. Use [useNavigation](/packages/composables.html#usenavigation) composable to `loadNavigationElements` to display a navigation configured in admin panel.
 2. Iterate over `navigationElements` array of categories and display them.
 3. Add some static links next-to dynamic ones if needed.
 4. Resolve URLs and implement dedicated pages for it.
@@ -75,14 +75,14 @@ loadNavigationElements({
 </style>
 ```
 
-[getCategoryUrl](../../../../packages/helpers.html#getcategoryurl) method imported from `helpers-next` package can extract a SEO Url or technical URL for given category.
+[getCategoryUrl](/packages/helpers.html#getcategoryurl) method imported from `helpers-next` package can extract a SEO Url or technical URL for given category.
 
 :::warning
 `getCategoryUrl` returns absolute path for corresponding category, which means you will get for example `/some-category/some-subcategory` and not the entire URL including domain.
 
 By design, the URL can point also to the Product or Landing Page.
-In order to resolve an entity assigned to each category path, utilize a [composable](../../../../packages/composables.html#usenavigation) dedicated for expected entity:
+In order to resolve an entity assigned to each category path, utilize a [composable](/packages/composables.html#usenavigation) dedicated for expected entity:
 
 1. `search` from `useNavigationSearch` to find entity type.
-2. use [dedicated composable](../../../routing.html#resolve-a-route-to-a-page) to process page resolving.
+2. use [dedicated composable](/getting-started/routing.html#resolve-a-route-to-a-page) to process page resolving.
    :::
diff --git a/apps/docs/src/getting-started/page-elements/examples/product-detail-page/index.md b/apps/docs/src/getting-started/page-elements/examples/product-detail-page/index.md
index 9c78d0bf..88298a9c 100644
--- a/apps/docs/src/getting-started/page-elements/examples/product-detail-page/index.md
+++ b/apps/docs/src/getting-started/page-elements/examples/product-detail-page/index.md
@@ -4,7 +4,7 @@
 Just copy the code snippet and paste it into your project. Sometimes it's useful to create a new component and use it in a higher level component like a page or a layout.
 :::
 
-See also the [complete guide](../../../e-commerce/product-detail-page.html) about the product detail page from the BUILDING section.
+See also the [complete guide](/getting-started/e-commerce/product-detail-page.html) about the product detail page from the BUILDING section.
 
 ## Simple Product Detail Page
 
diff --git a/apps/docs/src/getting-started/page-elements/images.md b/apps/docs/src/getting-started/page-elements/images.md
index 5db0507a..d2cf84f2 100644
--- a/apps/docs/src/getting-started/page-elements/images.md
+++ b/apps/docs/src/getting-started/page-elements/images.md
@@ -107,7 +107,7 @@ While a file is uploaded, it's been automatically resized for the current config
 
 ## Helpers
 
-There are few functions that could be used to extract some crucial information about the media in short way. For example [getMainImageUrl](../../packages/helpers.html#getmainimageurl) or [getMedia](../../packages/helpers.html#getmedia).
+There are few functions that could be used to extract some crucial information about the media in short way. For example [getMainImageUrl](/packages/helpers.html#getmainimageurl) or [getMedia](/packages/helpers.html#getmedia).
 
 Example how to work with Product's main image:
 
diff --git a/apps/docs/src/getting-started/routing.md b/apps/docs/src/getting-started/routing.md
index 1058a7bc..3b98e905 100644
--- a/apps/docs/src/getting-started/routing.md
+++ b/apps/docs/src/getting-started/routing.md
@@ -122,7 +122,7 @@ Thankfully, we do not need to do that in every case. Only the first request (han
 
 To create speaking links for products or categories, you must know the `seoPathInfo` from the `seoURLs` object. In some situations, you only have the ID of the product or category and then you may need to make an additional call to get the speaking link. This call costs time and can be omitted.
 
-We have created two new helper functions that can be used to avoid these extra calls. Just use [getCategoryRoute](../packages/helpers.html#getcategoryroute) and [getProductRoute](../packages/helpers.html#getproductroute) from helpers package. Use them in combination of `RouterLink` or `NuxtLink` in Vue.js or Nuxt.js projects.
+We have created two new helper functions that can be used to avoid these extra calls. Just use [getCategoryRoute](/packages/helpers.html#getcategoryroute) and [getProductRoute](/packages/helpers.html#getproductroute) from helpers package. Use them in combination of `RouterLink` or `NuxtLink` in Vue.js or Nuxt.js projects.
 
 ##### Example getCategoryRoute with NuxtLink
 
diff --git a/apps/docs/src/getting-started/templates/demo-store-template.md b/apps/docs/src/getting-started/templates/demo-store-template.md
index a8c07359..c23bc086 100644
--- a/apps/docs/src/getting-started/templates/demo-store-template.md
+++ b/apps/docs/src/getting-started/templates/demo-store-template.md
@@ -62,7 +62,7 @@ The demo store template is a boilerplate, so it will constantly be updated, as w
 
 One exception to the rule are CMS components. CMS components are handled as a separate package `cms-base` in Shopware Frontends and can be updated automatically. However, you can still override the components from the package in your custom project.
 
-<PageRef page="./../../framework/shopping-experiences.html" title="Customize CMS Components" sub="Override CMS components using the cms-base package" />
+<PageRef page="/framework/shopping-experiences.html" title="Customize CMS Components" sub="Override CMS components using the cms-base package" />
 
 ## Configure
 
diff --git a/apps/docs/src/installation.md b/apps/docs/src/installation.md
index 2d138c61..ea573943 100644
--- a/apps/docs/src/installation.md
+++ b/apps/docs/src/installation.md
@@ -1,3 +1,15 @@
+---
+head:
+  - - meta
+    - name: og:title
+      content: Installation
+  - - meta
+    - name: og:description
+      content: "Shopware Frontends is a framework for building custom, cloud-native Shopware Storefronts."
+nav:
+  hidden: true
+---
+
 <script setup>
 import stackblitzIcon from '.assets/framework-icons/stackblitz.png';
 </script>
diff --git a/apps/docs/src/resources/integrations/cms/index.md b/apps/docs/src/resources/integrations/cms/index.md
index 4c8a56dd..e058a8d7 100644
--- a/apps/docs/src/resources/integrations/cms/index.md
+++ b/apps/docs/src/resources/integrations/cms/index.md
@@ -19,14 +19,14 @@ nav:
 
 <div class="grid grid-cols-1 lg:grid-cols-2 gap-4 place-items-center">
   <div>
-    <a href="strapi" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 my-5 text-center">
+    <a href="strapi.html" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 my-5 text-center">
       <span class="hidden dark:block mb-2 text-2xl font-bold tracking-tight text-gray-900"><img src="../../../.assets/cms-icons/Strapi.full.logo.light.png" alt="Strapi Logo" class="my-8 h-14 inline" /></span>
       <span class="block dark:hidden mb-2 text-2xl font-bold tracking-tight text-gray-900"><img src="../../../.assets/cms-icons/Strapi.full.logo.dark.png" alt="Strapi Logo" class="my-8 h-14 inline" /></span>
       <p class="font-normal text-gray-700 dark:text-gray-400 h-40">Strapi is an open-source headless content management system (CMS). It's built on Node.js and provides a customizable API out of the box. Here's how you can integrate it into Composable Frontends.</p>
     </a>
   </div>
   <div>
-    <a href="storyblok" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 my-5 text-center">
+    <a href="storyblok.html" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 my-5 text-center">
       <span class="hidden dark:block mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><img src="../../../.assets/cms-icons/storyblok.light.svg" alt="storyblok Logo" class="my-8 h-14 inline" /></span>
       <span class="block dark:hidden mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><img src="../../../.assets/cms-icons/storyblok.dark.svg" alt="storyblok Logo" class="my-8 h-14 inline" /></span>
       <p class="font-normal text-gray-700 dark:text-gray-400 h-40">Storyblok is a headless Content Management System (CMS) that allows developers to manage and distribute content across multiple platforms. Use the Nuxt Module to integrate it into Composable Frontends.</p>
diff --git a/apps/docs/src/resources/integrations/cms/storyblok.md b/apps/docs/src/resources/integrations/cms/storyblok.md
index 98988387..6cd7d25e 100644
--- a/apps/docs/src/resources/integrations/cms/storyblok.md
+++ b/apps/docs/src/resources/integrations/cms/storyblok.md
@@ -19,7 +19,7 @@ nav:
 [<img src="../../../.assets/cms-icons/storyblok.dark.svg" alt="Storyblok Logo" class="mb-8 h-20 block dark:hidden" />](https://www.storyblok.com/docs/guide/introduction)
 
 Storyblok is a headless CMS that can be easily integrated into any Nuxt 3 application.
-On this page we explain the basics of how to integrate it into our [vue-blank template](../../../getting-started/templates/blank-template.html).
+On this page we explain the basics of how to integrate it into our [vue-blank template](/getting-started/templates/blank-template.html).
 
 ## Step by step guide
 
@@ -184,7 +184,7 @@ On this page we explain the basics of how to integrate it into our [vue-blank te
 
     <img src="../../../.assets/integrations/storyblok/storyblok-content-edit-home-page.png" alt="Storyblok content edit home page screenshot" class="border-1px border-#eeeeee rounded-md shadow-md my-8 hover:shadow-2xl hover:scale-105 transition duration-200" />
 
-11. Now start you local dev server and open [this page](http://localhost:3000/storyblok/home).  
+11. Now start you local dev server and open `http://localhost:3000/storyblok/home`.  
     You should see a page looking like the screen below.
     <img src="../../../.assets/integrations/storyblok/storyblok-home-page-in-nuxt.png" alt="Storyblok  home page in nuxt screenshot" class="border-1px border-#eeeeee rounded-md shadow-md my-8 hover:shadow-2xl hover:scale-105 transition duration-200" />
 
diff --git a/apps/docs/src/resources/integrations/cms/strapi.md b/apps/docs/src/resources/integrations/cms/strapi.md
index 8dac14ee..11a5d293 100644
--- a/apps/docs/src/resources/integrations/cms/strapi.md
+++ b/apps/docs/src/resources/integrations/cms/strapi.md
@@ -97,7 +97,7 @@ Now we can add our component to the layout.
 ## Fetching and displaying pages
 
 :::warning
-This example is written for the vue-demo-store template and assuming that you [implemented Multiple CMS middleware](../../../getting-started/cms/multiple-cms.html)
+This example is written for the vue-demo-store template and assuming that you [implemented Multiple CMS middleware](/getting-started/cms/multiple-cms.html#adding-middleware)
 :::
 
 Create new collection type `Page` on the Stripe admin site with fields:
diff --git a/apps/docs/src/resources/integrations/commercial/b2b-quote-management.md b/apps/docs/src/resources/integrations/commercial/b2b-quote-management.md
index 21ded933..4f429c55 100644
--- a/apps/docs/src/resources/integrations/commercial/b2b-quote-management.md
+++ b/apps/docs/src/resources/integrations/commercial/b2b-quote-management.md
@@ -26,7 +26,7 @@ In this chapter you will learn how to
 
 ## Quick reference
 
-- [useB2bQuoteManagement](../../../packages/composables/useB2bQuoteManagement) is a composable used for a quote management
+- [useB2bQuoteManagement](/packages/composables.html#useb2bquotemanagement) is a composable used for a quote management
 
 ## Request new quote
 
diff --git a/apps/docs/src/resources/integrations/commercial/custom-products.md b/apps/docs/src/resources/integrations/commercial/custom-products.md
index 7b93e46d..14b64c7a 100644
--- a/apps/docs/src/resources/integrations/commercial/custom-products.md
+++ b/apps/docs/src/resources/integrations/commercial/custom-products.md
@@ -13,4 +13,4 @@ nav:
   position: 30
 ---
 
-# Custom Products
+<!-- load: ../../../../../examples/commercial-customized-products/README.md -->
\ No newline at end of file
diff --git a/apps/docs/src/resources/integrations/commercial/index.md b/apps/docs/src/resources/integrations/commercial/index.md
index 76973fbd..1e10319e 100644
--- a/apps/docs/src/resources/integrations/commercial/index.md
+++ b/apps/docs/src/resources/integrations/commercial/index.md
@@ -17,7 +17,7 @@ nav:
 
 Here you will find all integration documents for the commercial features of Rise, Evolve and Beyond Plans for Shopware.
 
-- [B2B Quick-Order](b2b-quick-order)
-- [B2B Quote-Management](b2b-quote-management)
-- [Custom Products](custom-products)
-- [Digital Sales Rooms](digital-sales-rooms)
+- [B2B Quick-Order](b2b-quick-order.html)
+- [B2B Quote-Management](b2b-quote-management.html)
+- [Custom Products](custom-products.html)
+- [Digital Sales Rooms](digital-sales-rooms.html)
diff --git a/apps/docs/src/resources/integrations/payments/adyen.md b/apps/docs/src/resources/integrations/payments/adyen.md
index a1941e3d..671306e4 100644
--- a/apps/docs/src/resources/integrations/payments/adyen.md
+++ b/apps/docs/src/resources/integrations/payments/adyen.md
@@ -13,8 +13,6 @@ nav:
   position: 10
 ---
 
-
 [<img src="../../../.assets/payment-icons/adyen.png" alt="Adyen Logo" class="mb-8 h-20" />](https://docs.adyen.com/)
 
-
-<!-- load: ../../../../../examples/adyen-dropin-component/README.md -->
\ No newline at end of file
+<!-- load: ../../../../../examples/adyen-dropin-component/README.md -->
diff --git a/apps/docs/src/resources/integrations/payments/index.md b/apps/docs/src/resources/integrations/payments/index.md
index ede19316..ed55d80f 100644
--- a/apps/docs/src/resources/integrations/payments/index.md
+++ b/apps/docs/src/resources/integrations/payments/index.md
@@ -19,19 +19,19 @@ nav:
 
 <div class="grid grid-cols-1 lg:grid-cols-2 gap-4 place-items-center">
   <div>
-    <a href="mollie" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 mb-5 text-center">
+    <a href="mollie.html" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 mb-5 text-center">
       <span class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><img src="../../../.assets/payment-icons/mollie.webp" alt="mollie Logo" class="my-8 h-20 inline"/></span>
         <p class="font-normal text-gray-700 dark:text-gray-400 h-40">Easy payments and financial management for all companies with mollie. Here you will find instructions on how to set up mollie with Shopware and Composable Frontends.</p>
     </a>
   </div>
   <div>
-    <a href="paypal-express" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 mb-5 text-center">
+    <a href="paypal-express.html" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 mb-5 text-center">
       <span class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><img src="../../../.assets/payment-icons/paypal-express.png" alt="PayPal Express Logo" class="my-8 h-20 inline" /></span>
       <p class="font-normal text-gray-700 dark:text-gray-400 h-40">PayPal Express will increase your conversions. You can use PayPal Express with Shopware and Composable Frontends. Read more about this in this integration guide.</p>
     </a>
   </div>
   <div>
-    <a href="adyen" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 mb-5 text-center">
+    <a href="adyen.html" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow text-gray-700 hover:bg-gray-100 dark:bg-#242424 dark:border-#363636 dark:border-solid dark:hover:bg-#282828 mb-5 text-center">
       <span class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><img src="../../../.assets/payment-icons/adyen.png" alt="Adyen Logo" class="my-8 h-20 inline" /></span>
       <p class="font-normal text-gray-700 dark:text-gray-400 h-40">Global payments on every channel, data and financial management in a single solution. We have an example for you of how you can integrate Adyen into Composable Frontends.</p>
     </a>
diff --git a/apps/docs/src/resources/integrations/payments/mollie.md b/apps/docs/src/resources/integrations/payments/mollie.md
index a544413a..13a4cfd0 100644
--- a/apps/docs/src/resources/integrations/payments/mollie.md
+++ b/apps/docs/src/resources/integrations/payments/mollie.md
@@ -15,5 +15,4 @@ nav:
 
 [<img src="../../../.assets/payment-icons/mollie.webp" alt="mollie Logo" class="mb-8 h-20" />](https://docs.mollie.com/)
 
-
-<!-- load: ../../../../../examples/mollie-credit-card/README.md -->
\ No newline at end of file
+<!-- load: ../../../../../examples/mollie-credit-card/README.md -->
diff --git a/examples/adyen-dropin-component/README.md b/examples/adyen-dropin-component/README.md
index 0de39637..375b6d75 100644
--- a/examples/adyen-dropin-component/README.md
+++ b/examples/adyen-dropin-component/README.md
@@ -72,7 +72,7 @@ runtimeConfig: {
 
 ## Use Credit Card components
 
-[Open app.vue](./app.vue) file to see how it's working.
+[Open app.vue](https://github.com/shopware/frontends/blob/main/examples/adyen-dropin-component/app.vue) file to see how it's working.
 
 ## Development
 
diff --git a/examples/commercial-customized-products/README.md b/examples/commercial-customized-products/README.md
index d1f8a1e8..7e6c1f94 100644
--- a/examples/commercial-customized-products/README.md
+++ b/examples/commercial-customized-products/README.md
@@ -1,26 +1,77 @@
-# useAddToCart composable
+# Customized Products example
 
-This example should help get you started developing [Shopware Frontends](https://github.com/shopware/frontends).
+The example shows how to integrate a Vue.js project with the logic provided by Customized Products plugin. This is ready to run project containing all needed tools to make Custom Products extension working in a headless approach.
 
-## Customization
+## Features
 
-- edit [./src/App.vue](./src/App.vue) in order to change the current example
-- edit [./src/main.js](./src/main.js) in order to adjust Shopware Frontends plugin
+- 🚠 &nbsp;`ProductCustomizedProductConfigurator.vue` component to use in a Vue project
+- 🛤️ &nbsp;`useProductCustomizedProductConfigurator` composable function to work with the logic enabled by a plugin
 
-## Project Setup
+## Requirements
 
-```sh
-npm install
-```
+- A knowledge about [Custom Products](https://docs.shopware.com/en/shopware-6-en/extensions/customproducts) extension
+- Custom Products extension available
+- Some Vue.js project
 
-### Compile and Hot-Reload for Development
+## Setup
 
-```sh
-npm run dev
-```
+1. [Setup](https://frontends.shopware.com/getting-started/templates.html) your Vue template
+
+3. Prepare some customized products following the [documentation](https://docs.shopware.com/en/shopware-6-en/extensions/customproducts#add-template)
+
+## Usage
+
+In the example, the product with Custom Product is found and used in the template.
+
+`<ProductCustomizedProductConfigurator />` component utilizes features from `useProductCustomizedProductConfigurator` which takes the product from app's context and take care of the state of selected options.
+
+1. Import a component to display extended product options
+
+    ```js 
+    import ProductCustomizedProductConfigurator from "@/components/ProductCustomizedProductConfigurator.vue";
+    ```
+
+2. Register a product in Shopware context:
+
+    ```js
+    // product found by an api-client or a composable
+    useProduct(product);
+    ```
+3. Display the component
+
+    ```html
+    <!-- your Vue template -->
+    <ProductCustomizedProductConfigurator />
+    ```
 
-### Compile and Minify for Production
+### `useProductCustomizedProductConfigurator` API
+
+Available methods and properties provided by the composable function:
+
+`isActive` - is customized product enabled
+
+`customizedProduct` - the entire `Product` entity
+
+`state` -  state of the selected options
+
+`addToCart` - method for adding product to cart, using customized options in the payload
+
+`handleFileUpload` - helper for media type options, updates the state automatically
+
+
+Investigate more in the [component's source](https://github.com/shopware/frontends/blob/main/examples/commercial-customized-products/src/components/ProductCustomizedProductConfigurator.vue).
+
+## Run for development
 
 ```sh
-npm run build
+pnpm dev
 ```
+
+or...
+
+[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/shopware/frontends/tree/main/examples/commercial-customized-products?file=README.md)
+
+
+## Resources
+- [Custom Products](https://docs.shopware.com/en/shopware-6-en/extensions/customproducts) docs
+- [📖 &nbsp;Composable Frontends Documentation](https://frontends.shopware.com)
diff --git a/packages/cms-base/components/SwProductCard.vue b/packages/cms-base/components/SwProductCard.vue
index 10ce36e9..8824550c 100644
--- a/packages/cms-base/components/SwProductCard.vue
+++ b/packages/cms-base/components/SwProductCard.vue
@@ -83,11 +83,17 @@ const toggleWishlistProduct = async () => {
   isLoading.value = true;
 
   try {
-    if (!isInWishlist.value) await addToWishlist();
-    else await removeFromWishlist();
-    pushSuccess(
-      `${props.product?.translated?.name} ${isLoading.value ? translations.product.addedToWishlist : translations.product.removedFromTheWishlist}`,
-    );
+    if (!isInWishlist.value) {
+      await addToWishlist();
+      pushSuccess(
+        `${props.product?.translated?.name} ${translations.product.addedToWishlist}`,
+      );
+    } else {
+      await removeFromWishlist();
+      pushError(
+        `${props.product?.translated?.name} ${translations.product.removedFromTheWishlist}`,
+      );
+    }
   } catch (error) {
     if (error instanceof ApiClientError) {
       const reason = error.details.errors?.[0]?.detail
diff --git a/packages/composables/src/useCustomerOrders.ts b/packages/composables/src/useCustomerOrders.ts
index f6ea0fbb..c59ae438 100644
--- a/packages/composables/src/useCustomerOrders.ts
+++ b/packages/composables/src/useCustomerOrders.ts
@@ -1,5 +1,5 @@
-import { ref } from "vue";
-import type { Ref } from "vue";
+import { ref, computed } from "vue";
+import type { Ref, ComputedRef } from "vue";
 import { useShopwareContext } from "#imports";
 import type { Schemas } from "#shopware";
 
@@ -18,6 +18,18 @@ export type UseCustomerOrdersReturn = {
    * Fetches the orders list and assigns the result to the `orders` property
    */
   loadOrders(parameters?: Schemas["Criteria"]): Promise<void>;
+  /**
+   * Current page number
+   */
+  currentPage: ComputedRef<number>;
+  /**
+   * total pages
+   */
+  totalPages: ComputedRef<number>;
+  /**
+   * Limit of orders per page
+   */
+  limit: Ref<number>;
 };
 
 /**
@@ -30,21 +42,45 @@ export function useCustomerOrders(): UseCustomerOrdersReturn {
 
   const orders: Ref<Schemas["Order"][]> = ref([]);
 
+  const currentPaginationPage = ref<number>(1);
+  const limit = ref<number>(15);
+
+  const totalOrderItemsCount: Ref<number> = ref(0);
+
+  const currentParams = ref<Schemas["Criteria"]>({});
+
   const loadOrders = async (
     parameters: Schemas["Criteria"] = {},
   ): Promise<void> => {
+    const params = {
+      ...parameters,
+      limit: limit.value,
+    };
+    currentParams.value = params;
     const fetchedOrders = await apiClient.invoke("readOrder post /order", {
-      body: parameters,
+      body: { ...params, "total-count-mode": "exact" },
     });
     orders.value = fetchedOrders.data.orders.elements;
+    totalOrderItemsCount.value = fetchedOrders.data.orders.total ?? 0;
   };
 
-  const changeCurrentPage = async (pageNumber: number | string) =>
-    await loadOrders({ page: +pageNumber });
+  const changeCurrentPage = async (pageNumber: number) => {
+    await loadOrders({ ...currentParams.value, page: pageNumber });
+    currentPaginationPage.value = pageNumber;
+  };
+
+  const currentPage = computed(() => currentPaginationPage.value);
+
+  const totalPages = computed(() => {
+    return Math.ceil(totalOrderItemsCount.value / limit.value);
+  });
 
   return {
     orders,
     changeCurrentPage,
     loadOrders,
+    currentPage,
+    totalPages,
+    limit,
   };
 }
diff --git a/packages/composables/src/useSyncWishlist.ts b/packages/composables/src/useSyncWishlist.ts
index 28c9c588..10ce8a27 100644
--- a/packages/composables/src/useSyncWishlist.ts
+++ b/packages/composables/src/useSyncWishlist.ts
@@ -2,12 +2,15 @@ import { ref, computed } from "vue";
 import type { Ref, ComputedRef } from "vue";
 import { useShopwareContext } from "#imports";
 import { ApiClientError } from "@shopware/api-client";
+import type { Schemas } from "#shopware";
 
 export type UseSyncWishlistReturn = {
   /**
    * Get products from wishlist
    */
-  getWishlistProducts(): void;
+  getWishlistProducts(
+    defaultSearchCriteria?: Schemas["Criteria"],
+  ): Promise<void>;
   /**
    * Merge products with wishlist already existing in API wishlist
    */
@@ -28,9 +31,15 @@ export type UseSyncWishlistReturn = {
    * Wishlist items count
    */
   count: ComputedRef<number>;
+  /**
+   * Current page number
+   */
+  currentPage: ComputedRef<number>;
 };
 
 const _wishlistItems: Ref<string[]> = ref([]);
+const _currentPage: Ref<number> = ref(1);
+const totalWishlistItemsCount: Ref<number> = ref(0);
 
 /**
  * Composable to manage wishlist via API
@@ -39,7 +48,6 @@ const _wishlistItems: Ref<string[]> = ref([]);
  */
 export function useSyncWishlist(): UseSyncWishlistReturn {
   const { apiClient } = useShopwareContext();
-
   async function addToWishlistSync(id: string) {
     await apiClient.invoke(
       "addProductOnWishlist post /customer/wishlist/add/{productId}",
@@ -62,14 +70,19 @@ export function useSyncWishlist(): UseSyncWishlistReturn {
    * Fetch wishlist items
    * Only for logged-in users
    */
-  async function getWishlistProducts() {
+  async function getWishlistProducts(
+    defaultSearchCriteria?: Schemas["Criteria"],
+  ) {
     try {
       const response = await apiClient.invoke(
         "readCustomerWishlist post /customer/wishlist",
+        { body: { ...defaultSearchCriteria, "total-count-mode": "exact" } },
       );
       _wishlistItems.value = [
         ...response.data.products.elements.map((element) => element.id),
       ];
+      totalWishlistItemsCount.value = response.data.products.total ?? 0;
+      _currentPage.value = response.data.products.page ?? 1;
     } catch (e) {
       if (e instanceof ApiClientError) {
         // If 404 ignore printing error and reset wishlist
@@ -89,7 +102,8 @@ export function useSyncWishlist(): UseSyncWishlistReturn {
   }
 
   const items = computed(() => _wishlistItems.value);
-  const count = computed(() => items.value.length);
+  const count = computed(() => totalWishlistItemsCount.value);
+  const currentPage = computed(() => _currentPage.value);
 
   return {
     getWishlistProducts,
@@ -98,5 +112,6 @@ export function useSyncWishlist(): UseSyncWishlistReturn {
     mergeWishlistProducts,
     items,
     count,
+    currentPage,
   };
 }
diff --git a/packages/composables/src/useWishlist.ts b/packages/composables/src/useWishlist.ts
index 9378a9db..72a61759 100644
--- a/packages/composables/src/useWishlist.ts
+++ b/packages/composables/src/useWishlist.ts
@@ -1,9 +1,11 @@
 /**
  * Template composable
  */
-import { computed } from "vue";
+import { computed, ref } from "vue";
 import type { ComputedRef } from "vue";
 import { useUser, useLocalWishlist, useSyncWishlist } from "#imports";
+import type { Schemas } from "#shopware";
+import { defu } from "defu";
 
 export type UseWishlistReturn = {
   /**
@@ -13,7 +15,7 @@ export type UseWishlistReturn = {
   /**
    * Get products list added to wishlist
    */
-  getWishlistProducts(): void;
+  getWishlistProducts(query?: Schemas["Criteria"]): Promise<void>;
   /**
    * Clear wishlist
    */
@@ -23,9 +25,21 @@ export type UseWishlistReturn = {
    */
   items: ComputedRef<string[]>;
   /**
-   * Wishlist items count
+   * Current page number
+   */
+  currentPage: ComputedRef<number>;
+  /**
+   * total pages count
+   */
+  totalPagesCount: ComputedRef<number>;
+  /**
+   * total wishlist items count
    */
   count: ComputedRef<number>;
+  /**
+   * Indicates if the wishlist can be synced
+   */
+  canSyncWishlist: ComputedRef<boolean>;
 };
 
 /**
@@ -38,7 +52,6 @@ export function useWishlist(): UseWishlistReturn {
   const canSyncWishlist = computed(
     () => isLoggedIn.value && !isGuestSession.value,
   );
-
   const {
     getWishlistProducts: getWishlistProductsLocal,
     items: itemsLocal,
@@ -50,16 +63,34 @@ export function useWishlist(): UseWishlistReturn {
     items: itemsSync,
     mergeWishlistProducts: mergeWishlistProductsSync,
     removeFromWishlistSync,
+    count: countSync,
+    currentPage,
   } = useSyncWishlist();
 
-  const getWishlistProducts = async () => {
+  const limit = ref<number>(15);
+
+  const getWishlistProducts = async (query?: Schemas["Criteria"]) => {
+    const wishlistQuery = {} as Schemas["Criteria"];
+    if (query) {
+      defu(wishlistQuery, query);
+    }
+    if (query?.limit) {
+      limit.value = query.limit;
+    }
+
     if (canSyncWishlist.value) {
-      await getWishlistProductsSync();
+      await getWishlistProductsSync(query);
     } else {
-      await getWishlistProductsLocal();
+      getWishlistProductsLocal();
     }
   };
 
+  const items = computed(() =>
+    canSyncWishlist.value ? itemsSync.value : itemsLocal.value,
+  );
+
+  const totalPagesCount = computed(() => Math.ceil(count.value / limit.value));
+
   const clearWishlist = async () => {
     if (canSyncWishlist.value) {
       await Promise.all(items.value.map((id) => removeFromWishlistSync(id)));
@@ -74,19 +105,24 @@ export function useWishlist(): UseWishlistReturn {
       await mergeWishlistProductsSync(itemsLocal.value);
       clearWishlist();
     }
-    getWishlistProductsSync();
+    await getWishlistProductsSync();
   };
 
-  const items = computed(() =>
-    canSyncWishlist.value ? itemsSync.value : itemsLocal.value,
-  );
-  const count = computed(() => items.value.length);
+  const count = computed(() => {
+    if (canSyncWishlist.value) {
+      return countSync.value;
+    }
+    return items.value.length;
+  });
 
   return {
     mergeWishlistProducts,
     getWishlistProducts,
     clearWishlist,
     items,
+    currentPage,
+    totalPagesCount,
     count,
+    canSyncWishlist,
   };
 }
diff --git a/templates/vue-demo-store/app.vue b/templates/vue-demo-store/app.vue
index 1481c5e6..6a2c4766 100644
--- a/templates/vue-demo-store/app.vue
+++ b/templates/vue-demo-store/app.vue
@@ -109,9 +109,15 @@ if (languages.value?.elements.length && router.currentRoute.value.name) {
   provide("urlPrefix", prefix);
 }
 
+if (import.meta.client) {
+  // getting the wishlist products should not block SSR
+  if (!(router.currentRoute.value.name as string).includes("wishlist")) {
+    getWishlistProducts(); // initial page loading
+  }
+}
+
 onMounted(() => {
   refreshCart();
-  getWishlistProducts();
 });
 </script>
 
diff --git a/templates/vue-demo-store/components/FrontendDetailPage.vue b/templates/vue-demo-store/components/FrontendDetailPage.vue
index 2a833d7c..2623bec4 100644
--- a/templates/vue-demo-store/components/FrontendDetailPage.vue
+++ b/templates/vue-demo-store/components/FrontendDetailPage.vue
@@ -7,7 +7,7 @@ const props = defineProps<{
 
 const { search } = useProductSearch();
 
-const { data: productResponse } = await useAsyncData(
+const { data: productResponse, error } = await useAsyncData(
   "cmsProduct" + props.navigationId,
   async () => {
     const productResponse = await search(props.navigationId, {
@@ -18,8 +18,8 @@ const { data: productResponse } = await useAsyncData(
 );
 
 if (!productResponse.value) {
-  console.error("No product found for navigationId: " + props.navigationId);
-  throw new Error("No product found for navigationId: " + props.navigationId);
+  console.error("[FrontendDetailPage.vue]", error.value?.message);
+  throw error.value;
 }
 
 useProductJsonLD(productResponse.value.product);
diff --git a/templates/vue-demo-store/components/FrontendLandingPage.vue b/templates/vue-demo-store/components/FrontendLandingPage.vue
index a93d33ef..a4f9f1ac 100644
--- a/templates/vue-demo-store/components/FrontendLandingPage.vue
+++ b/templates/vue-demo-store/components/FrontendLandingPage.vue
@@ -8,7 +8,7 @@ const props = defineProps<{
 
 const { search } = useLandingSearch();
 
-const { data: landingResponse } = await useAsyncData(
+const { data: landingResponse, error } = await useAsyncData(
   "cmsLanding" + props.navigationId,
   async () => {
     const landingPage = await search(props.navigationId, {
@@ -18,10 +18,13 @@ const { data: landingResponse } = await useAsyncData(
   },
 );
 
-if (typeof landingResponse?.value !== null) {
-  const landingPage = landingResponse as Ref<Schemas["LandingPage"]>;
-  useCmsHead(landingPage, { mainShopTitle: "Shopware Frontends Demo Store" });
+if (!landingResponse?.value) {
+  console.error("[FrontendLandingPage.vue]", error.value?.message);
+  throw error.value;
 }
+
+const landingPage = landingResponse as Ref<Schemas["LandingPage"]>;
+useCmsHead(landingPage, { mainShopTitle: "Shopware Frontends Demo Store" });
 </script>
 
 <template>
diff --git a/templates/vue-demo-store/components/FrontendNavigationPage.vue b/templates/vue-demo-store/components/FrontendNavigationPage.vue
index b0df6c2f..c0a5b589 100644
--- a/templates/vue-demo-store/components/FrontendNavigationPage.vue
+++ b/templates/vue-demo-store/components/FrontendNavigationPage.vue
@@ -12,7 +12,7 @@ const props = defineProps<{
 const { search } = useCategorySearch();
 const route = useRoute();
 
-const { data: categoryResponse } = await useAsyncData(
+const { data: categoryResponse, error } = await useAsyncData(
   "cmsNavigation" + props.navigationId,
   async () => {
     const category = await search(props.navigationId, {
@@ -24,13 +24,18 @@ const { data: categoryResponse } = await useAsyncData(
     return category;
   },
 );
-if (categoryResponse.value) {
-  const breadcrumbs = getCategoryBreadcrumbs(categoryResponse.value, {
-    startIndex: 2,
-  });
-  useBreadcrumbs(breadcrumbs);
+
+if (!categoryResponse.value) {
+  console.error("[FrontendNavigationPage.vue]", error.value?.message);
+  throw error.value;
 }
 
+const breadcrumbs = getCategoryBreadcrumbs(categoryResponse.value, {
+  startIndex: 2,
+});
+
+useBreadcrumbs(breadcrumbs);
+
 const { category } = useCategory(categoryResponse as Ref<Schemas["Category"]>);
 useCmsHead(category, { mainShopTitle: "Shopware Frontends Demo Store" });
 </script>
diff --git a/templates/vue-demo-store/components/account/AccountRegisterForm.vue b/templates/vue-demo-store/components/account/AccountRegisterForm.vue
index c2526419..1d1b2530 100644
--- a/templates/vue-demo-store/components/account/AccountRegisterForm.vue
+++ b/templates/vue-demo-store/components/account/AccountRegisterForm.vue
@@ -18,7 +18,7 @@ const loading = ref<boolean>();
 const doubleOptInBox = ref();
 const showDoubleOptInBox = ref(false);
 const { t } = useI18n();
-if (process.client && isLoggedIn.value) {
+if (import.meta.client && isLoggedIn.value) {
   // redirect to account page if user is logged in
   navigateTo({ path: "/account" });
 }
diff --git a/templates/vue-demo-store/components/layout/LayoutHeader.vue b/templates/vue-demo-store/components/layout/LayoutHeader.vue
index 0e391795..565ddcfe 100644
--- a/templates/vue-demo-store/components/layout/LayoutHeader.vue
+++ b/templates/vue-demo-store/components/layout/LayoutHeader.vue
@@ -51,7 +51,7 @@ const sidebarController = useModal();
                 class="w-7 h-7 i-carbon-favorite text-secondary-600 hover:text-primary hover:animate-count-infinite hover:animate-heart-beat"
               />
               <span
-                v-if="wishlistCount > 0"
+                v-if="wishlistCount"
                 class="text-3 font-sm text-white absolute bg-red-600 rounded-full min-w-5 min-h-5 top-0 right-0 leading-5"
               >
                 {{ wishlistCount }}
diff --git a/templates/vue-demo-store/components/layout/LayoutStoreSearch.vue b/templates/vue-demo-store/components/layout/LayoutStoreSearch.vue
index f41b335e..53aab0c3 100644
--- a/templates/vue-demo-store/components/layout/LayoutStoreSearch.vue
+++ b/templates/vue-demo-store/components/layout/LayoutStoreSearch.vue
@@ -52,7 +52,7 @@ const showSuggest = computed(() => {
   return typingQuery.value.length >= 3 && active.value;
 });
 
-if (process.client) {
+if (import.meta.client) {
   onClickOutside(searchContainer, () => {
     active.value = false;
   });
diff --git a/templates/vue-demo-store/composables/useAuthGuard.ts b/templates/vue-demo-store/composables/useAuthGuard.ts
index 2444e4af..0bb7421d 100644
--- a/templates/vue-demo-store/composables/useAuthGuard.ts
+++ b/templates/vue-demo-store/composables/useAuthGuard.ts
@@ -11,7 +11,7 @@ export function useAuthGuardRedirection(params?: { to: string }) {
   watch(
     isLoggedIn,
     (isLoggedIn) => {
-      if (process.client && !isLoggedIn) {
+      if (import.meta.client && !isLoggedIn) {
         if (!params?.to) {
           router.push({ path: "/" });
           pushInfo(
diff --git a/templates/vue-demo-store/error.vue b/templates/vue-demo-store/error.vue
index 96ae40fc..655feb2e 100644
--- a/templates/vue-demo-store/error.vue
+++ b/templates/vue-demo-store/error.vue
@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import type { NuxtError } from "#app";
 import type { RouteObject } from "@shopware-pwa/composables-next";
 
 defineOptions({
@@ -6,11 +7,11 @@ defineOptions({
 });
 
 const props = defineProps<{
-  error: {
+  error: NuxtError<{
     statusCode: number;
     statusMessage: string;
     message: string;
-  };
+  }>;
 }>();
 
 const isMaintenanceMode = computed(() => {
@@ -36,8 +37,10 @@ try {
   };
 
   errMessage =
-    props.error.statusMessage ||
-    errorMessageMap[props.error.statusCode as keyof typeof errorMessageMap];
+    errorMessageMap[props.error.statusCode as keyof typeof errorMessageMap] ||
+    props.error.message;
+
+  console.error("[error.vue]:", props.error?.message);
 
   if (props.error.statusCode === 412) {
     // setting a timeout here to ensure we are the last error message in terminal
@@ -73,11 +76,14 @@ const errorMessage = isFormattedError ? errMessage : props.error.message;
         </h1>
         <p
           v-if="errorMessage"
-          class="text-xl md:text-3xl font-semibold mt-4 mb-8"
+          class="text-xl md:text-3xl font-semibold mt-4 mb-6"
         >
           {{ errorMessage }}
         </p>
         <DevOnly>
+          <div class="text-xs text-gray-500">
+            {{ $t("setup.check_console") }}
+          </div>
           <div class="block m-4">
             <p>{{ $t("setup.problems") }}</p>
             <p>
diff --git a/templates/vue-demo-store/i18n/de-DE/account.json b/templates/vue-demo-store/i18n/de-DE/account.json
index a83f8e23..f557da48 100644
--- a/templates/vue-demo-store/i18n/de-DE/account.json
+++ b/templates/vue-demo-store/i18n/de-DE/account.json
@@ -44,7 +44,9 @@
       "orderDate": "Bestelldatum",
       "orderStatus": "Bestellstatus",
       "paymentMethod": "Zahlungsmethode",
-      "repeatOrder": "Bestellung wiederholen"
+      "repeatOrder": "Bestellung wiederholen",
+      "order": "Bestellung",
+      "orders": "Bestellungen"
     },
     "personalData": {
       "infoBox": "Bearbeiten Sie Ihre Daten unten, damit Ihr Konto immer auf dem neuesten Stand ist.",
diff --git a/templates/vue-demo-store/i18n/de-DE/general.json b/templates/vue-demo-store/i18n/de-DE/general.json
index 2778714b..69f6e114 100644
--- a/templates/vue-demo-store/i18n/de-DE/general.json
+++ b/templates/vue-demo-store/i18n/de-DE/general.json
@@ -37,6 +37,7 @@
     "503": "Der Server ist derzeit sehr beschäftigt"
   },
   "setup": {
+    "check_console": "Im Konsolenprotokoll finden Sie die Details 🖵",
     "problems": "Probleme beim aufsetzen? 😬",
     "support_start": "Schau mal auf die",
     "support_page": "Troubleshooting-Seite",
diff --git a/templates/vue-demo-store/i18n/en-GB/account.json b/templates/vue-demo-store/i18n/en-GB/account.json
index 1f7c0c22..be72a08b 100644
--- a/templates/vue-demo-store/i18n/en-GB/account.json
+++ b/templates/vue-demo-store/i18n/en-GB/account.json
@@ -46,7 +46,9 @@
       "orderDate": "Order date",
       "orderStatus": "Order status",
       "paymentMethod": "Payment method",
-      "repeatOrder": "Repeat order"
+      "repeatOrder": "Repeat order",
+      "order": "Order",
+      "orders": "Orders"
     },
     "personalData": {
       "infoBox": "Feel free to edit any of your details below so your account is always up to date",
diff --git a/templates/vue-demo-store/i18n/en-GB/general.json b/templates/vue-demo-store/i18n/en-GB/general.json
index b3756b76..c24c9824 100644
--- a/templates/vue-demo-store/i18n/en-GB/general.json
+++ b/templates/vue-demo-store/i18n/en-GB/general.json
@@ -39,6 +39,7 @@
     "503": "Server is really busy right now"
   },
   "setup": {
+    "check_console": "Check the console log to see the details 🖵",
     "problems": "Problems during setup? 😬",
     "support_start": "Checkout the",
     "support_page": "troubleshooting page",
diff --git a/templates/vue-demo-store/i18n/pl-PL/account.json b/templates/vue-demo-store/i18n/pl-PL/account.json
index 07374470..cdb3aa25 100644
--- a/templates/vue-demo-store/i18n/pl-PL/account.json
+++ b/templates/vue-demo-store/i18n/pl-PL/account.json
@@ -44,7 +44,9 @@
       "orderDate": "Data zamówienia",
       "orderStatus": "Status zamówienia",
       "paymentMethod": "Metoda płatności",
-      "repeatOrder": "Powtórz zamówienie"
+      "repeatOrder": "Powtórz zamówienie",
+      "order": "zamówienie",
+      "orders": "Zamówienia"
     },
     "personalData": {
       "infoBox": "Edytuj swoje dane poniżej, aby twoje konto zawsze było aktualne",
diff --git a/templates/vue-demo-store/i18n/pl-PL/general.json b/templates/vue-demo-store/i18n/pl-PL/general.json
index c6fb321d..516e21a8 100644
--- a/templates/vue-demo-store/i18n/pl-PL/general.json
+++ b/templates/vue-demo-store/i18n/pl-PL/general.json
@@ -39,6 +39,7 @@
     "503": "Serwer jest teraz bardzo zajęty"
   },
   "setup": {
+    "check_console": "Sprawdź console log żeby sprawdzić szczegóły 🖵",
     "problems": "Problemy podczas konfiguracji? 😬",
     "support_start": "Sprawdź",
     "support_page": "strona rozwiązywania problemów",
diff --git a/templates/vue-demo-store/pages/[...all].vue b/templates/vue-demo-store/pages/[...all].vue
index f82164f8..c57fecc0 100644
--- a/templates/vue-demo-store/pages/[...all].vue
+++ b/templates/vue-demo-store/pages/[...all].vue
@@ -21,7 +21,7 @@ const { data: seoResult } = await useAsyncData(
   "cmsResponse" + routePath,
   async () => {
     // For client links if the history state contains seo url information we can omit the api call
-    if (process.client) {
+    if (import.meta.client) {
       if (history.state?.routeName) {
         return {
           routeName: history.state?.routeName,
@@ -34,6 +34,15 @@ const { data: seoResult } = await useAsyncData(
   },
 );
 
+if (!seoResult.value?.foreignKey) {
+  console.error("[...all].vue:", `No data found in API for ${routePath}`);
+
+  throw createError({
+    statusCode: 404,
+    statusMessage: `No data fetched from API for ${routePath}`,
+  });
+}
+
 const { routeName, foreignKey } = useNavigationContext(
   seoResult as Ref<Schemas["SeoUrl"]>,
 );
diff --git a/templates/vue-demo-store/pages/account/order.vue b/templates/vue-demo-store/pages/account/order.vue
index 7493cfa8..8bc18337 100644
--- a/templates/vue-demo-store/pages/account/order.vue
+++ b/templates/vue-demo-store/pages/account/order.vue
@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import { useRoute, useRouter } from "vue-router";
+
 defineOptions({
   name: "OrderHistory",
 });
@@ -6,8 +8,41 @@ definePageMeta({
   layout: "account",
 });
 
-const { orders, loadOrders } = useCustomerOrders();
+const {
+  orders,
+  loadOrders,
+  changeCurrentPage,
+  totalPages,
+  currentPage,
+  limit,
+} = useCustomerOrders();
 const { t } = useI18n();
+const route = useRoute();
+const router = useRouter();
+
+const defaultLimit = 15;
+const defaultPage = 1;
+
+type Translations = {
+  account: {
+    noOrders: string;
+    perPage: string;
+    order: {
+      order: string;
+      orders: string;
+    };
+  };
+};
+let translations: Translations = {
+  account: {
+    noOrders: "No orders found 😔",
+    perPage: "Per Page:",
+    order: {
+      order: "Order",
+      orders: "Orders",
+    },
+  },
+};
 
 useBreadcrumbs([
   {
@@ -20,8 +55,36 @@ useBreadcrumbs([
   },
 ]);
 
+limit.value = route.query.limit ? Number(route.query.limit) : defaultLimit;
+
+const changeLimit = async (ev: Event) => {
+  const select = ev.target as HTMLSelectElement;
+  limit.value = +select.value;
+
+  await router.push({
+    query: {
+      ...route.query,
+      p: defaultPage,
+      limit: select.value,
+    },
+  });
+  changeCurrentPage(+route.query.p!);
+};
+
+const changePage = async (page: number) => {
+  await router.push({
+    query: {
+      ...route.query,
+      p: page,
+    },
+  });
+  await changeCurrentPage(page);
+};
+
 await useAsyncData("getOrders", () => {
   return loadOrders({
+    limit: limit.value,
+    page: route.query.p ? Number(route.query.p) : defaultPage,
     associations: {
       stateMachineState: {},
     },
@@ -41,5 +104,57 @@ await useAsyncData("getOrders", () => {
       {{ $t("account.orderHistoryHeader") }}
     </h1>
     <AccountOrder v-for="order in orders" :key="order.id" :order="order" />
+    <div v-if="orders.length === 0" class="text-center text-secondary-600">
+      {{ translations.account.noOrders }}
+    </div>
+    <div
+      class="grid grid-cols-1 lg:flex lg:justify-center lg- gap-4 md:gap-6 lg:gap-8 p-4 md:p-6 lg:p-8"
+    >
+      <div class="text-center place-self-center">
+        <SwPagination
+          :total="totalPages"
+          :current="Number(currentPage)"
+          @change-page="changePage"
+        />
+      </div>
+      <div class="text-center place-self-center mt-2 lg:mt-0">
+        <div class="inline-block align-top text-center md:text-left">
+          <label for="limit" class="inline mr-4">{{
+            translations.account.perPage
+          }}</label>
+          <select
+            id="limit"
+            v-model="limit"
+            name="limitchoices"
+            class="inline appearance-none bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
+            @change="changeLimit"
+          >
+            <option :value="1">1 {{ translations.account.order.order }}</option>
+            <option :value="15">
+              15 {{ translations.account.order.orders }}
+            </option>
+            <option :value="30">
+              30 {{ translations.account.order.orders }}
+            </option>
+            <option :value="45">
+              45 {{ translations.account.order.orders }}
+            </option>
+          </select>
+          <div
+            class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
+          >
+            <svg
+              class="fill-current h-4 w-4"
+              xmlns="http://www.w3.org/2000/svg"
+              viewBox="0 0 20 20"
+            >
+              <path
+                d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
+              />
+            </svg>
+          </div>
+        </div>
+      </div>
+    </div>
   </div>
 </template>
diff --git a/templates/vue-demo-store/pages/login.vue b/templates/vue-demo-store/pages/login.vue
index 010d357d..da158447 100644
--- a/templates/vue-demo-store/pages/login.vue
+++ b/templates/vue-demo-store/pages/login.vue
@@ -11,7 +11,7 @@ const localePath = useLocalePath();
 const { formatLink } = useInternationalization(localePath);
 
 onBeforeMount(async () => {
-  if (process.client && isLoggedIn.value) {
+  if (import.meta.client && isLoggedIn.value) {
     // redirect to account page if user is logged in
     navigateTo({ path: "/account" });
   }
diff --git a/templates/vue-demo-store/pages/wishlist.vue b/templates/vue-demo-store/pages/wishlist.vue
index c9eac9db..f463d17e 100644
--- a/templates/vue-demo-store/pages/wishlist.vue
+++ b/templates/vue-demo-store/pages/wishlist.vue
@@ -1,23 +1,55 @@
 <script setup lang="ts">
 import type { Schemas } from "#shopware";
+import defu from "defu";
+import { useRoute, useRouter } from "vue-router";
 
+const defaultLimit = 15;
+const defaultPage = 1;
+
+type Translations = {
+  listing: {
+    noProducts: string;
+    perPage: string;
+    product: string;
+    products: string;
+  };
+};
+let translations: Translations = {
+  listing: {
+    noProducts: "No products found 😔",
+    perPage: "Per Page:",
+    product: "Product",
+    products: "Products",
+  },
+};
+translations = defu(translations, useCmsTranslations()) as Translations;
+
+const {
+  items,
+  clearWishlist,
+  getWishlistProducts,
+  currentPage,
+  totalPagesCount,
+  canSyncWishlist,
+} = useWishlist();
 defineOptions({
   name: "WishlistPage",
 });
-
-const { items, clearWishlist } = useWishlist();
 const { apiClient } = useShopwareContext();
 const products = ref<Schemas["Product"][]>([]);
 const isLoading = ref(false);
 const { t } = useI18n();
 const localePath = useLocalePath();
 const { formatLink } = useInternationalization(localePath);
+const route = useRoute();
+const router = useRouter();
 useBreadcrumbs([
   {
     name: t("breadcrumbs.wishlist"),
     path: "/wishlist",
   },
 ]);
+const limit = ref(route.query.limit ? Number(route.query.limit) : defaultLimit);
 const clearWishlistHandler = async () => {
   try {
     isLoading.value = true;
@@ -44,6 +76,30 @@ const loadProductsByItemIds = async (itemIds: string[]): Promise<void> => {
   isLoading.value = false;
 };
 
+const changeLimit = async (limit: Event) => {
+  const select = limit.target as HTMLSelectElement;
+  const query = {
+    ...route.query,
+    limit: Number(select.value),
+    page: defaultPage,
+  };
+
+  await router.push({
+    query: query,
+  });
+  await getWishlistProducts(query);
+};
+
+const changePage = async (page: number) => {
+  await router.push({
+    query: {
+      page: page,
+      limit: limit.value,
+    },
+  });
+  await getWishlistProducts(route.query);
+};
+
 watch(
   items,
   (items, oldItems) => {
@@ -59,6 +115,40 @@ watch(
     immediate: true,
   },
 );
+
+const initalRoute = defu(route);
+watch(
+  () => route,
+  (newRoute) => {
+    if (initalRoute.path !== newRoute.path) {
+      getWishlistProducts(); // update wishlist items when you leave wishlist page
+      return;
+    }
+    if (Object.keys(newRoute.query).length > 0) {
+      return;
+    }
+    // this fires to reset the page when query are removed/empty on client side navigation for the same page (without hard reload)
+    getWishlistProducts({
+      limit: defaultLimit,
+      page: defaultPage,
+    });
+    limit.value = defaultLimit;
+  },
+  { deep: true },
+);
+
+onMounted(() => {
+  const route = useRoute();
+  const limit = ref(
+    route.query.limit ? Number(route.query.limit) : defaultLimit,
+  );
+  const page = ref(route.query.page ? Number(route.query.page) : defaultPage);
+  // this request is needed to start with the first page and the current limit or default limit for the pagination on wishlist page
+  getWishlistProducts({
+    limit: limit.value,
+    page: page.value,
+  });
+});
 </script>
 
 <template>
@@ -87,13 +177,12 @@ watch(
         >
           {{ $t("wishlist.clearWishlist") }}
         </button>
-        <div
-          class="grid grid-cols-1 gap-y-10 gap-x-6 sm:grid-cols-2 lg:grid-cols-4 xl:gap-x-8"
-        >
+        <div class="flex justify-center flex-wrap p-4 md:p-6 lg:p-8">
           <ProductCard
             v-for="product in products"
             :key="product.id"
             :product="product"
+            class="w-full mb-8 sm:w-3/7 lg:w-2/7 2xl:w-7/24 mr-0 sm:mr-8 mb-8"
           />
         </div>
       </div>
@@ -118,6 +207,50 @@ watch(
       </div>
     </div>
   </div>
+  <div
+    v-if="canSyncWishlist && products.length > 0"
+    class="grid grid-cols-1 lg:flex lg:justify-center lg- gap-4 md:gap-6 lg:gap-8 p-4 md:p-6 lg:p-8"
+  >
+    <div class="text-center place-self-center">
+      <SwPagination
+        :total="Number(totalPagesCount)"
+        :current="currentPage"
+        @change-page="changePage"
+      />
+    </div>
+    <div class="text-center place-self-center mt-2 lg:mt-0">
+      <div class="inline-block align-top text-center md:text-left">
+        <label for="limit" class="inline mr-4">{{
+          translations.listing.perPage
+        }}</label>
+        <select
+          id="limit"
+          v-model="limit"
+          name="limitchoices"
+          class="inline appearance-none bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
+          @change="changeLimit"
+        >
+          <option :value="1">1 {{ translations.listing.product }}</option>
+          <option :value="15">15 {{ translations.listing.products }}</option>
+          <option :value="30">30 {{ translations.listing.products }}</option>
+          <option :value="45">45 {{ translations.listing.products }}</option>
+        </select>
+        <div
+          class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700"
+        >
+          <svg
+            class="fill-current h-4 w-4"
+            xmlns="http://www.w3.org/2000/svg"
+            viewBox="0 0 20 20"
+          >
+            <path
+              d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
+            />
+          </svg>
+        </div>
+      </div>
+    </div>
+  </div>
 </template>
 
 <style scoped></style>


Test patch:
diff --git a/packages/composables/src/useCustomerOrders.test.ts b/packages/composables/src/useCustomerOrders.test.ts
index afac1f29..cfa108ad 100644
--- a/packages/composables/src/useCustomerOrders.test.ts
+++ b/packages/composables/src/useCustomerOrders.test.ts
@@ -20,12 +20,15 @@ describe("useCustomerOrders", () => {
     injections.apiClient.invoke.mockResolvedValue({
       data: { orders: { elements: [] } },
     });
+    vm.limit = 3;
     vm.changeCurrentPage(2);
     expect(injections.apiClient.invoke).toHaveBeenCalledWith(
       expect.stringContaining("readOrder"),
       expect.objectContaining({
         body: {
+          limit: 3,
           page: 2,
+          "total-count-mode": "exact",
         },
       }),
     );
diff --git a/packages/composables/src/useWishlist.test.ts b/packages/composables/src/useWishlist.test.ts
index 7501d2be..a5c393d1 100644
--- a/packages/composables/src/useWishlist.test.ts
+++ b/packages/composables/src/useWishlist.test.ts
@@ -1,37 +1,184 @@
 import { describe, expect, it, vi } from "vitest";
 import { useWishlist } from "./useWishlist";
+import { useSyncWishlist } from "./useSyncWishlist";
+import { useUser } from "./useUser";
 import { useSetup } from "./_test";
+import { computed, ref } from "vue";
+import type { Schemas } from "#shopware";
+
+const getMockedUser = (isLoggedIn: boolean, isGuestSession: boolean) =>
+  ({
+    isLoggedIn: ref(isLoggedIn),
+    isGuestSession: ref(isGuestSession),
+  }) as ReturnType<typeof useUser>;
+
+vi.mock("./useLocalWishlist.ts", () => ({
+  useLocalWishlist() {
+    return {
+      getWishlistProducts: () => undefined,
+      items: {
+        value: [
+          "local-testId-1",
+          "local-testId-2",
+          "local-testId-3",
+          "local-testId-4",
+          "local-testId-5",
+          "local-testId-6",
+          "local-testId-7",
+          "local-testId-8",
+          "local-testId-9",
+          "local-testId-10",
+          "local-testId-11",
+          "local-testId-12",
+          "local-testId-13",
+          "local-testId-14",
+          "local-testId-15",
+          "local-testId-16",
+          "local-testId-17",
+        ],
+      },
+      clearWishlist: () => undefined,
+      count: ref(17),
+    };
+  },
+}));
+
+vi.mock("./useSyncWishlist.ts");
+vi.mock("./useUser");
 
 describe("useWishlist - not logged in user", () => {
-  vi.mock("./useLocalWishlist.ts", () => ({
-    useLocalWishlist() {
-      return {
-        getWishlistProducts: () => undefined,
-        items: { value: ["local-testId333"] },
-        clearWishlist: () => undefined,
-      };
-    },
-  }));
-
-  it("mergeWishlistProducts", async () => {
+  it("mergeWishlistProducts", () => {
+    vi.mocked(useUser).mockReturnValue(getMockedUser(false, true));
+    vi.mocked(useSyncWishlist).mockReturnValue({
+      getWishlistProducts: () => new Promise<void>((resolve) => resolve()),
+      items: computed((): string[] => {
+        return ["test1"];
+      }),
+      addToWishlistSync: () => undefined,
+      mergeWishlistProducts: () => undefined,
+      removeFromWishlistSync: () => undefined,
+      count: computed(() => 17),
+      currentPage: computed(() => 2),
+    });
+    const { vm } = useSetup(() => useWishlist());
+
+    vm.clearWishlist();
+    vm.getWishlistProducts({ page: 2, limit: 15 });
+    vm.mergeWishlistProducts();
+
+    expect(vm.items.length).toBe(17);
+    expect(vm.count).toBe(17);
+    expect(Number(vm.totalPagesCount)).toBe(2);
+  });
+
+  it("getWishlistProducts", async () => {
+    vi.mocked(useUser).mockReturnValue(getMockedUser(false, true));
+    vi.mocked(useSyncWishlist).mockReturnValue({
+      getWishlistProducts: () => new Promise<void>((resolve) => resolve()),
+      items: computed((): string[] => {
+        return ["test1"];
+      }),
+      addToWishlistSync: () => undefined,
+      mergeWishlistProducts: () => undefined,
+      removeFromWishlistSync: () => undefined,
+      count: computed(() => 15),
+      currentPage: computed(() => 2),
+    });
     const { vm } = useSetup(() => useWishlist());
 
-    expect(vm.items.length).toBe(1);
-    expect(vm.count).toBe(1);
-    await vm.clearWishlist();
-    await vm.getWishlistProducts();
-    await vm.mergeWishlistProducts();
+    vm.getWishlistProducts();
+
+    expect(vm.items).toEqual([
+      "local-testId-1",
+      "local-testId-2",
+      "local-testId-3",
+      "local-testId-4",
+      "local-testId-5",
+      "local-testId-6",
+      "local-testId-7",
+      "local-testId-8",
+      "local-testId-9",
+      "local-testId-10",
+      "local-testId-11",
+      "local-testId-12",
+      "local-testId-13",
+      "local-testId-14",
+      "local-testId-15",
+      "local-testId-16",
+      "local-testId-17",
+    ]);
+
+    const query = {
+      limit: 1,
+      page: 2,
+    } as Schemas["Criteria"];
+    vm.getWishlistProducts(query);
+
+    expect(Number(vm.currentPage)).toBe(2);
   });
 });
 
 describe("useWishlist - logged in user", () => {
-  it("mergeWishlistProducts", async () => {
+  it("mergeWishlistProducts", () => {
+    vi.mocked(useUser).mockReturnValue(getMockedUser(true, false));
+    vi.mocked(useSyncWishlist).mockReturnValue({
+      getWishlistProducts: () => new Promise<void>((resolve) => resolve()),
+      items: computed((): string[] => {
+        return [
+          "local-test-1",
+          "local-test-2",
+          "local-test-3",
+          "local-test-4",
+          "local-test-5",
+          "local-test-6",
+          "local-test-7",
+          "local-test-8",
+          "local-test-9",
+          "local-test-10",
+          "local-test-11",
+          "local-test-12",
+          "local-test-13",
+          "local-test-14",
+          "local-test-15",
+        ];
+      }),
+      addToWishlistSync: () => undefined,
+      mergeWishlistProducts: () => undefined,
+      removeFromWishlistSync: () => undefined,
+      count: computed(() => 15),
+      currentPage: computed(() => 1),
+    });
+    const { vm } = useSetup(() => useWishlist());
+
+    vm.clearWishlist();
+    vm.getWishlistProducts();
+    vm.mergeWishlistProducts();
+
+    expect(vm.items.length).toBe(15);
+    expect(vm.count).toBe(15);
+  });
+
+  it("getWishlistProducts", () => {
+    vi.mocked(useUser).mockReturnValue(getMockedUser(true, false));
     const { vm } = useSetup(() => useWishlist());
+    vm.getWishlistProducts();
 
-    expect(vm.items.length).toBe(1);
-    expect(vm.count).toBe(1);
-    await vm.clearWishlist();
-    await vm.getWishlistProducts();
-    await vm.mergeWishlistProducts();
+    expect(vm.items).toEqual([
+      "local-test-1",
+      "local-test-2",
+      "local-test-3",
+      "local-test-4",
+      "local-test-5",
+      "local-test-6",
+      "local-test-7",
+      "local-test-8",
+      "local-test-9",
+      "local-test-10",
+      "local-test-11",
+      "local-test-12",
+      "local-test-13",
+      "local-test-14",
+      "local-test-15",
+    ]);
   });
 });